/**
 * @file      uc_wiota_at_com.c
 * @brief
 * @version   1.0
 * @date      2023.12.14
 *
 * @copyright Copyright (c) 2018 UCchip Technology Co.,Ltd. All rights reserved.
 *
 * Change Logs:
 * Date          Author       Notes
 * 2023.12.14    ucchip       the first version
 *
 */

#include "config_api.h"
#include "drv_gw_power.h"
#include "manager_queue.h"
#include "wiota_flashing.h"
#include <at.h>
#include <rtdevice.h>
#include <rtthread.h>
#include <stdio.h>
#include <uc_wiota_api.h>

#define DBG_ENABLE
#define DBG_SECTION_NAME "at_com"
#include "uc_log.h"

//{ WIoTa AT urc回调事件类型，见urc_table
// WIoTa发送数据成功
#define WIOTA_EVENT_SEND_OK (1L << 1)
// WIoTa发送数据失败
#define WIOTA_EVENT_SEND_FAIL (1L << 2)
// WIoTa发送数据超时
#define WIOTA_EVENT_SEND_TIMEOUT (1L << 3)
// WIoTa sync paging完成标识
#define WIOTA_EVENT_SYNC_PAGING_OK (1L << 5)
//}

//{ WIoTa AT客户端配置信息
// 主控连接AP的串口为uart6（Master uart6<---->AP uart0）
#define WIOTA_AT_CLIENT_NAME "uart6"
// 单次AT响应数据的buffer大小，如果超过该大小会提示full
#define WIOTA_AT_RECV_BUFSZ (512)
// AT指令执行失败后重试的次数
#define WIOTA_AT_RETRY_CNT (3)
// 单次AT指令等待响应的超时时间
#define WIOTA_AT_RES_TIMEOUT (4000)
//}

// WIoTa扫频超时时间，（非全扫的时间，根据静态数据表配置最多为16个频点的扫频时间）
#define WIOTA_SCAN_FREQ_TIMEOUT (10 * 60 * 1000)

/**
 * @brief 用于AT+WIOTAHOPPING命令的枚举
 *
 */
enum
{
    HOPPING_SET_FREQ = 0, /**< 设置跳频频点 */
    HOPPING_SET_MODE = 1, /**< 设置跳频模式 */
};

/**
 * @brief 用于AT+WIOTABLACKLIST命令的枚举
 *
 */
enum
{
    ADD_BLACKLIST = 0,    /**< 添加黑名单 */
    REMOVE_BLACKLIST = 1, /**< 移除黑名单 */
};

/**
 * @brief 用于AT+WIOTALOG命令的枚举
 *
 */
enum
{
    LOG_CLOSE = 0,     /**< 关闭协议栈串口log */
    LOG_OPEN = 1,      /**< 打开协议栈串口log */
    LOG_UART0 = 2,     /**< 切换log口到uart0 */
    LOG_UART1 = 3,     /**< 切换log口到uart1 */
    LOG_SPI_CLOSE = 4, /**< 关闭协议栈SPI log */
    LOG_SPI_OPEN = 5,  /**< 打开协议栈SPI log */
};

/**
 * @brief WIoTa发送类型枚举
 *
 */
typedef enum
{
    SEND_TYPE_UC = 0, /**< 发送单播 */
    SEND_TYPE_BC = 1, /**< 发送广播（包括普通广播和OTA） */
    SEND_TYPE_MC = 2, /**< 发送组播 */
    SEND_TYPE_MAX
} send_type_e;

// WIoTa扫频结果队列结构
typedef struct
{
    unsigned char *data; /**< 扫频结果字符串 */
    int data_len;        /**< 扫频结果字符串长度 */
    int num;             /**< 扫频的频点个数 */
    int result;          /**< 扫频结果 */
} scan_data_t;

//{WIoTa扰码相关
// 用于userid位置查询所需生成扰码的随机初始值
#define ALGO_MULTIPLIER 0x15a4e35
#define ALGO_INCREMENT 0x1

typedef union
{
    unsigned int data; // WIoTa扰码（跟user_id和子网id相关）
    struct
    {
        unsigned int reserve : 2;
        unsigned int slot_idx : 3;
        unsigned int burst_idx : 3;
        unsigned int group_idx : 3;
    } bit_info; // user_id在帧结构的位置bit信息
} scramble_u;   // WIoTa扰码联合体

static unsigned int g_algo_seed = 100;
//}

// WIoTa AT客户端句柄
static at_client_t g_wiota_at_client = RT_NULL;
// WIoTa AT urc回调事件句柄
static rt_event_t g_wiota_at_event = RT_NULL;
// WIoTa 扫频结果接收队列句柄
static rt_mq_t g_wiota_scan_mq = RT_NULL;

// 黑名单链表头
static uc_blacklist_t g_blacklist = {0};

//{ WIoTa回调函数，仅在非阻塞调用时生效
// 接收数据回调
static uc_recv g_recv_cb = RT_NULL;
static uc_recv_detail g_recv_detail_cb = RT_NULL;
// IoTE掉线提示回调
static uc_iote_drop g_drop_cb = RT_NULL;
// 发送数据回调（单播、广播、OTA和组播）
static uc_send_callback g_send_cb[SEND_TYPE_MAX] = {RT_NULL};
// 扫频结果回调
static uc_scan_callback g_scan_cb = RT_NULL;
// sync paging控制消息接收回调
static uc_paging_ctrl_callback g_sync_paging_ctrl_cb = RT_NULL;
// sync paging执行结果回调
static uc_paging_callback g_sync_paging_cb = RT_NULL;
// 授时信息回调
static uc_time_service_callback g_ts_cb = RT_NULL;
static uc_time_service_info_callback g_ts_info_cb = RT_NULL;
// AP帧号刷新回调
static uc_fn_refresh_callback g_fn_refresh_cb = RT_NULL;
//}

// AP低功耗唤醒配置信息（AP可以发送连续唤醒信号唤醒对应配置的IoTE）
// Note：具体低功耗配置请查阅相关文档
static uc_lpm_tx_cfg_t g_lpm_tx_cfg_info = {
    .freq = 160,        // 唤醒频点
    .spectrum_idx = 3,  // 频谱
    .bandwidth = 1,     // 带宽
    .symbol_length = 1, // WIoTa符号长度
    .awaken_id = 30,    // 唤醒ID
    .reserved = 0,      // 对齐预留位
    .send_time = 1000,  // 唤醒信号持续时间
};

/**
 * @brief 伪随机种子
 *
 * @param seed_set 随机种子
 */
static void uc_algo_srand(unsigned int seed_set)
{
    g_algo_seed = seed_set;
}

/**
 * @brief 生成一个32位的伪随机种子
 *
 * @return unsigned int 32位的伪随机种子
 */
static unsigned int uc_algo_rand(void)
{
    g_algo_seed = g_algo_seed * ALGO_MULTIPLIER + ALGO_INCREMENT;
    return ((g_algo_seed >> 16) & 0x7FFF);
}

/**
 * @brief 生成伪随机一个32位整形数
 *
 * @return unsigned int 伪随机一个32位整形数
 */
static unsigned int uc_algo_rand_u32(void)
{
    return ((uc_algo_rand() & 0xFF) << 24) | ((uc_algo_rand() & 0xFF) << 16) | ((uc_algo_rand() & 0xFF) << 8) | (uc_algo_rand() & 0xFF);
}

/**
 * @brief 计算num的n次幂
 *
 */
static unsigned int uc_nth_power(unsigned int num, unsigned int n)
{
    unsigned int s = 1;

    for (unsigned int i = 0; i < n; i++)
    {
        s *= num;
    }
    return s;
}

/**
 * @brief 将特定的数字string转为一个32位的整型
 *
 */
static void uc_string_to_int(unsigned int num_len, unsigned int num, const unsigned char *p_start, unsigned char *array)
{
    unsigned char *temp = NULL;
    unsigned int len = 0;
    unsigned int nth = num_len;

    if (RT_NULL == p_start || RT_NULL == array || 0 == num_len)
    {
        TRACE_E("%s line %d input para error", __FUNCTION__, __LINE__);
        return;
    }

    temp = (unsigned char *)rt_malloc(num_len);
    if (temp == NULL)
    {
        TRACE_E("%s line %d malloc fail", __FUNCTION__, __LINE__);
        return;
    }

    for (len = 0; len < num_len; len++)
    {
        temp[len] = p_start[len] - '0';
        array[num] += uc_nth_power(10, nth - 1) * temp[len];
        nth--;
    }
    rt_free(temp);
    temp = NULL;
}

/**
 * @brief 将特定的一组数字string转换为数组
 *
 */
static unsigned int uc_string_to_array(unsigned char *string, unsigned char *array)
{
    unsigned char *p_start = string;
    unsigned char *p_end = string;
    unsigned int num = 0;
    unsigned int num_len = 0;

    if (RT_NULL == string || RT_NULL == array)
    {
        TRACE_E("%s line %d input para error", __FUNCTION__, __LINE__);
        return 0;
    }

    while (*p_start != '\0')
    {
        while (*p_end != '\0')
        {
            if (*p_end == ',')
            {
                uc_string_to_int(num_len, num, p_start, array);
                num++;
                p_end++;
                p_start = p_end;
                num_len = 0;
            }
            num_len++;
            p_end++;
        }

        uc_string_to_int(num_len, num, p_start, array);
        num++;
        p_start = p_end;
    }
    return num;
}

/**
 * @brief 字符串翻转
 *
 */
static char *uc_str_reverse(char *str)
{
    char temp;
    char *p_start = str;
    char *p_end = str;

    while (*p_end)
        ++p_end;
    p_end--;

    while (p_end > p_start)
    {
        temp = *p_start;
        *p_start++ = *p_end;
        *p_end-- = temp;
    }
    return str;
}

/**
 * @brief 将一个整型转化为string
 *
 */
static char *uc_itoa(int num)
{
    int i = 0, is_ng = 0;
    static char str[100];

    if ((is_ng = num) < 0)
    {
        num = -num;
    }
    do
    {
        str[i++] = num % 10 + '0';
        num = num / 10;
    } while (num > 0);

    if (is_ng < 0)
    {
        str[i++] = '-';
    }
    str[i] = '\0';

    return uc_str_reverse(str);
}

/**
 * @brief 将数组转换为string
 *
 */
static void uc_array_to_string(unsigned char *array, int array_len, unsigned char *string)
{
    int str_temp_len = 0;
    int offset = 0;
    unsigned char str_temp[10] = {0};

    if (RT_NULL == string || RT_NULL == array)
    {
        TRACE_E("%s line %d input para error", __FUNCTION__, __LINE__);
        return;
    }

    for (int i = 0; i < array_len; i++)
    {
        rt_strncpy((char *)str_temp, uc_itoa(array[i]), 10);
        str_temp_len = rt_strlen((char *)str_temp);
        rt_strncpy((char *)string + offset, (char *)str_temp, str_temp_len);
        offset += str_temp_len;
        if (i < array_len - 1)
        {
            rt_strncpy((char *)string + offset, ",", 1);
            offset += 1;
        }
    }
}

/**
 * @brief WIoTa版本号解析函数
 *
 */
static int uc_wiota_parse_version_info(const char *in_buf, int in_buf_len, char *out_buf1, char *out_buf2)
{
    const char *p_start = in_buf;
    const char *p_end = in_buf;
    int cp_len = 0;
    int find_flag = 0;

    if (RT_NULL == in_buf || 0 == in_buf_len)
    {
        TRACE_E("%s line %d input para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }

    while (*p_start != '\0')
    {
        if (*p_start == ':' && find_flag == 0)
        {
            p_end = p_start + 1;
            find_flag = 1;
        }

        if (*p_start == ',')
        {
            cp_len = p_start - p_end;
            if (out_buf1)
            {
                rt_strncpy(out_buf1, p_end, cp_len);
                out_buf1[cp_len] = '\0';
            }
            if (out_buf2)
            {
                rt_strncpy(out_buf2, p_start + 1, in_buf_len - cp_len);
                out_buf1[in_buf_len - cp_len] = '\0';
            }

            return RT_EOK;
        }

        p_start++;
    }
    return -RT_ERROR;
}

/**
 * @brief 触发urc回调后，发送一个事件
 *
 * @param event 发送事件类型
 * @return int  0表示成功，非0表示失败
 */
static int uc_wiota_event_send(unsigned int event)
{
    return (int)rt_event_send(g_wiota_at_event, event);
}

/**
 * @brief 等待接收事件
 *
 * @param event   要接收的事件类型
 * @param timeout 接收超时时间
 * @param option  时间选项
 * @return int    接收到的事件类型
 */
static int uc_wiota_event_recv(unsigned int event, unsigned int timeout, unsigned char option)
{
    int result = RT_EOK;
    rt_uint32_t recved;

    result = rt_event_recv(g_wiota_at_event, event, option | RT_EVENT_FLAG_CLEAR, timeout, &recved);
    if (result != RT_EOK)
    {
        return -RT_ETIMEOUT;
    }

    return recved;
}

/**
 * @brief WIoTa黑名单初始化
 *
 */
static void uc_wiota_blacklist_init()
{
    rt_slist_init(&g_blacklist.node);
}

/**
 * @brief WIoTa黑名单反初始化
 *
 */
void uc_wiota_blacklist_deinit(void)
{
    rt_slist_t *node_next = g_blacklist.node.next;

    while (node_next != NULL)
    {
        uc_blacklist_t *curr_node = rt_slist_entry(node_next, uc_blacklist_t, node);
        rt_free(curr_node);
        curr_node = RT_NULL;
        node_next = node_next->next;
    }

    rt_slist_init(&g_blacklist.node);
}

/**
 * @brief exec和setup类AT指令执行接口
 *
 * @param cmd  完整的AT指令字符串
 * @return int 0表示成功，非0表示失败
 */
static int uc_wiota_cmd_exec(char *cmd)
{
    struct at_response *resp = RT_NULL;
    int result = RT_EOK;
    unsigned int index = 0;

    // 参数检查
    RT_ASSERT(g_wiota_at_client);
    if (RT_NULL == cmd)
    {
        TRACE_E("%s line %d input para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }

    // 创建一个接收AT指令执行结果结构体指针
    resp = at_create_resp(AT_CMD_MAX_LEN, 0, rt_tick_from_millisecond(WIOTA_AT_RES_TIMEOUT));
    if (resp == RT_NULL)
    {
        TRACE_E("%s line %d create resp fail", __FUNCTION__, __LINE__);
        return -RT_ENOMEM;
    }

    // 执行AT指令，最多三次
    for (index = 0; index < WIOTA_AT_RETRY_CNT; index++)
    {
        // 执行AT指令，成功返回0
        if (at_obj_exec_cmd(g_wiota_at_client, resp, cmd) == 0)
        {
            break;
        }
    }

    // 超过三次，命令执行失败
    if (index >= WIOTA_AT_RETRY_CNT)
    {
        TRACE_E("cmd %s error.", cmd);
        result = -RT_ERROR;
    }

    // 释放接收AT指令执行结果结构体指针
    at_delete_resp(resp);

    return result;
}

/**
 * @brief query类AT指令执行接口
 *
 * @param cmd           查询类AT命令
 * @param key_words     查询结果返回关键字
 * @param resp_expr     查询结果表达式
 * @param out_value     期望查询结果接收buf
 * @param out_value_num 期望查询结果个数
 * @return int          0表示成功，非0表示失败
 */
static int uc_wiota_cmd_query(char *cmd, char *key_words, const char *resp_expr, unsigned int *out_value, unsigned int out_value_num)
{
    struct at_response *resp = RT_NULL;
    int result = RT_EOK;
    unsigned int index = 0;

    // 参数检查
    RT_ASSERT(g_wiota_at_client);
    if (RT_NULL == cmd || RT_NULL == key_words || RT_NULL == resp_expr || RT_NULL == out_value || 0 == out_value_num)
    {
        TRACE_E("%s line %d input para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }

    // 创建一个接收AT指令执行结果结构体指针
    resp = at_create_resp(AT_CMD_MAX_LEN, 0, rt_tick_from_millisecond(WIOTA_AT_RES_TIMEOUT));
    if (resp == RT_NULL)
    {
        TRACE_E("%s line %d create resp fail", __FUNCTION__, __LINE__);
        return -RT_ENOMEM;
    }

    // 执行AT指令，最多三次
    for (index = 0; index < WIOTA_AT_RETRY_CNT; index++)
    {
        // 执行AT指令，成功返回0
        if (at_obj_exec_cmd(g_wiota_at_client, resp, cmd) == 0)
        {
            // 根据实际的AT指令和期待返回的结果，解析接收buf数据，得到查询结果，大于0表示成功
            if (out_value_num == 1 && at_resp_parse_line_args_by_kw(resp, key_words, resp_expr, &out_value[0]) > 0)
            {
                break;
            }
            else if (out_value_num == 2 && at_resp_parse_line_args_by_kw(resp, key_words, resp_expr, &out_value[0], &out_value[1]) > 0)
            {
                break;
            }
            else if (out_value_num == 3 && at_resp_parse_line_args_by_kw(resp, key_words, resp_expr, &out_value[0], &out_value[1], &out_value[2]) > 0)
            {
                break;
            }
            else if (out_value_num == 6 && at_resp_parse_line_args_by_kw(resp, key_words, resp_expr,
                                                                         &out_value[0], &out_value[1],
                                                                         &out_value[2], &out_value[3],
                                                                         &out_value[4], &out_value[5]) > 0)
            {
                break;
            }
            else if (out_value_num == 10 && at_resp_parse_line_args_by_kw(resp, key_words, resp_expr,
                                                                          &out_value[0], &out_value[1],
                                                                          &out_value[2], &out_value[3],
                                                                          &out_value[4], &out_value[5],
                                                                          &out_value[6], &out_value[7],
                                                                          &out_value[8], &out_value[9]) > 0)
            {
                break;
            }
        }
    }

    // 超过三次，命令执行失败
    if (index >= WIOTA_AT_RETRY_CNT)
    {
        TRACE_E("cmd %s error.", cmd);
        result = -RT_ERROR;
    }

    // 释放接收AT指令执行结果结构体指针
    at_delete_resp(resp);

    return result;
}

/**
 * @brief 获取WIoTa的版本号，构建时间等信息
 *
 * @param wiota_version_8088 AP8088的协议栈版本
 * @param git_info_8088      AP8088的git最新一条的信息
 * @param make_time_8088     AP8088的协议栈版本构建时间
 * @param wiota_version_8288 AP8288的协议栈版本
 * @param git_info_8288      AP8288的git最新一条的信息
 * @param make_time_8288     AP8288的协议栈版本构建时间
 * @param cce_version        AP CCE版本
 * @return int               0表示成功，非0表示失败
 */
int uc_wiota_get_version(char *wiota_version_8088, char *git_info_8088, char *make_time_8088,
                         char *wiota_version_8288, char *git_info_8288, char *make_time_8288,
                         int *cce_version)
{
    struct at_response *resp = RT_NULL;
    int result = RT_EOK;
    unsigned int index = 0;

    // 参数检查，传入的参数可为空
    RT_ASSERT(g_wiota_at_client);

    do
    {
        // 创建一个接收AT指令执行结果结构体指针
        resp = at_create_resp(WIOTA_AT_RECV_BUFSZ, 0, rt_tick_from_millisecond(WIOTA_AT_RES_TIMEOUT));
        if (resp == RT_NULL)
        {
            TRACE_E("%s line %d create resp fail", __FUNCTION__, __LINE__);
            result = -RT_ENOMEM;
            break;
        }

        // 执行AT指令，最多三次
        for (index = 0; index < WIOTA_AT_RETRY_CNT; index++)
        {
            // 执行AT指令，成功返回0
            if (at_obj_exec_cmd(g_wiota_at_client, resp, "AT+WIOTAVERSION?") == 0)
            {
                const char *ver_info = at_resp_get_line(resp, 1);
                int ver_info_len = rt_strlen(ver_info);

                const char *git_info = at_resp_get_line(resp, 2);
                int git_info_len = rt_strlen(git_info);

                const char *make_time = at_resp_get_line(resp, 3);
                int make_time_len = rt_strlen(make_time);

                // 解析接收buf，返回解析结果
                if (uc_wiota_parse_version_info(ver_info, ver_info_len, wiota_version_8088, wiota_version_8288) < 0)
                {
                    TRACE_E("+WIOTAVERSION error.");
                }

                if (uc_wiota_parse_version_info(git_info, git_info_len, git_info_8088, git_info_8288) < 0)
                {
                    TRACE_E("+GITINFO error.");
                }

                if (uc_wiota_parse_version_info(make_time, make_time_len, make_time_8088, make_time_8288) < 0)
                {
                    TRACE_E("+TIME error.");
                }

                if (at_resp_parse_line_args_by_kw(resp, "+CCEVERSION:", "+CCEVERSION:%x", cce_version) < 0)
                {
                    TRACE_E("+CCEVERSION error.");
                }
                break;
            }
        }

        // 超过三次，命令执行失败
        if (index >= WIOTA_AT_RETRY_CNT)
        {
            TRACE_E("cmd AT+WIOTAVERSION? error.");
            result = -RT_ERROR;
            break;
        }
    } while (0);

    // 释放接收AT指令执行结果结构体指针
    at_delete_resp(resp);

    return result;
}

/**
 * @brief WIoTa协议栈初始化接口
 *
 * @return int 0表示成功，非0表示失败
 */
int uc_wiota_init(void)
{
    int result = RT_EOK;

    result = uc_wiota_cmd_exec("AT+WIOTAINIT");

    if (result == RT_EOK)
    {
        uc_wiota_blacklist_init();
    }
    return result;
}

/**
 * @brief WIoTa协议栈运行接口
 *
 * @return int 0表示成功，非0表示失败
 */
int uc_wiota_run(void)
{
    return uc_wiota_cmd_exec("AT+WIOTARUN=1");
}

/**
 * @brief WIoTa协议栈退出接口
 *
 * @return int 0表示成功，非0表示失败
 */
int uc_wiota_exit(void)
{
    return uc_wiota_cmd_exec("AT+WIOTARUN=0");
}

/**
 * @brief 使能AP电源
 *
 */
void uc_wiota_power_on(void)
{
    ap_power_init();
    TRACE_I("ap power on");
}

/**
 * @brief 关闭AP电源
 *
 */
void uc_wiota_power_off(void)
{
    ap_power_deinit();
    TRACE_I("ap power off");
}

/**
 * @brief AP模组重启
 *
 */
void uc_wiota_reboot(void)
{
    ap_power_reset();
    TRACE_I("ap reboot");
}

/**
 * @brief 使能同步助手电源
 *
 */
void uc_wiota_sync_assistant_power_on(void)
{
    sync_assistant_power_init();
    TRACE_I("sync assistant power on");
}

/**
 * @brief 关闭同步助手电源
 *
 */
void uc_wiota_sync_assistant_power_off(void)
{
    sync_assistant_power_deinit();
    TRACE_I("sync assistant power off");
}

/**
 * @brief 同步助手模组重启
 *
 */
void uc_wiota_sync_assistant_reboot(void)
{
    sync_assistant_power_reset();
    TRACE_I("sync assistant reboot");
}

/**
 * @brief 注册IoTE掉线提示回调函数，当IoTE接入后，在连接态时间内未在和AP交互，AP会将该IoTE踢掉，称为IoTE掉线
 *
 * @param callback 掉线提示回调函数指针
 */
void uc_wiota_register_iote_dropped_callback(uc_iote_drop callback)
{
    g_drop_cb = callback;
}

/**
 * @brief 注册IoTE上行数据接收回调函数，当AP收到IoTE上行数据并收完后，触发回调，将接收到的数据传给应用层
 *
 * @param callback 上行数据接收回调函数指针
 */
void uc_wiota_register_recv_data_callback(uc_recv callback)
{
    g_recv_cb = callback;
    if (callback)
    {
        g_recv_detail_cb = RT_NULL;
    }
}

/**
 * @brief 注册IoTE上行数据接收回调函数，当AP收到IoTE上行数据并收完后，触发回调，将接收到的数据传给应用层，
 *        相较于uc_wiota_register_recv_data_callback上报信息更加详细，且两个回调只会生效一个，后者生效
 *
 * @param callback 上行数据接收回调函数指针
 */
void uc_wiota_register_recv_data_detail_callback(uc_recv_detail callback)
{
    g_recv_detail_cb = callback;
    if (callback)
    {
        g_recv_cb = RT_NULL;
    }
    uc_wiota_cmd_exec("AT+WIOTAREGNEWCB=1,0");
}

/**
 * @brief 设置WIoTa子网配置
 *
 * @param config WIoTa子网配置结构体指针
 * @return int   0表示成功，非0表示失败
 */
int uc_wiota_set_system_config(sub_system_config_t *config)
{
    char cmd_buf[100];

    if (RT_NULL == config)
    {
        TRACE_E("%s line %d input para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }

    // 由于AP侧不支持解析负数，但功率可能为负数，所以这里加了一个基础值20，AP解析后会自动减去20
    rt_sprintf(cmd_buf, "AT+WIOTACONFIG=%d,%d,%d,%d,%d,%d,%d,%d,%d,%x",
               config->power + 20, config->id_len, config->symbol_length,
               config->dlul_ratio, config->bt_value, config->group_number,
               config->spectrum_idx, config->old_subsys_v, config->bitscb,
               config->subsystem_id);

    return uc_wiota_cmd_exec(cmd_buf);
}

/**
 * @brief 获取WIoTa子网配置
 *
 * @param config WIoTa子网配置结构体指针
 * @return int   0表示成功，非0表示失败
 */
int uc_wiota_get_system_config(sub_system_config_t *config)
{
    int result = RT_EOK;
    unsigned int out_value[10] = {0};

    if (RT_NULL == config)
    {
        TRACE_E("%s line %d input para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }

    result = uc_wiota_cmd_query("AT+WIOTACONFIG?",
                                "+WIOTACONFIG:",
                                "+WIOTACONFIG:%d,%d,%d,%d,%d,%d,%d,%d,%d,0x%x",
                                out_value, 10);
    if (result == RT_EOK)
    {
        config->power = out_value[0];
        config->id_len = out_value[1];
        config->symbol_length = out_value[2];
        config->dlul_ratio = out_value[3];
        config->bt_value = out_value[4];
        config->group_number = out_value[5];
        config->spectrum_idx = out_value[6];
        config->old_subsys_v = out_value[7];
        config->bitscb = out_value[8];
        config->subsystem_id = out_value[9];
        config->pp = 1;
    }

    return result;
}

/**
 * @brief 设置WIoTa连接态超时时间，IoTE接入到超时时间结束，称为连接态，在连接态期间收到的消息称为连接态消息，只要IoTE和AP有交互就会重置连接态时间，
 *        超过连接态时间未交互，IoTE掉线
 *
 * @param active_s 连接态超时时间，单位：秒
 * @return int     0表示成功，非0表示失败
 */
int uc_wiota_set_active_time(unsigned int active_s)
{
    char cmd_buf[100];

    TRACE_D("set_active_time %u", active_s);

    rt_sprintf(cmd_buf, "AT+WIOTAACTIVETIME=%d", active_s);

    return uc_wiota_cmd_exec(cmd_buf);
}

/**
 * @brief 获取WIoTa连接态超时时间
 *
 * @return int 连接态超时时间
 */
unsigned int uc_wiota_get_active_time(void)
{
    unsigned int active_time = 0;

    uc_wiota_cmd_query("AT+WIOTAACTIVETIME?", "+WIOTAACTIVETIME:", "+WIOTAACTIVETIME:%d", &active_time, 1);

    return active_time;
}

/**
 * @brief 单独设置AP发送功率接口，功率支持热设置（AP运行中也可设置）
 *
 * @param power    AP发射功率，范围0~29dbm
 * @return int     0表示成功，非0表示失败
 */
int uc_wiota_set_ap_tx_power(signed char power)
{
    char cmd_buf[100];

    if (0 > power || 29 < power)
    {
        TRACE_E("%s line %d input para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }

    TRACE_D("set_tx_power %d", power);

    rt_sprintf(cmd_buf, "AT+WIOTAPOW=%d", power + 20);

    return uc_wiota_cmd_exec(cmd_buf);
}

/**
 * @brief 设置WIoTa工作频点，不支持热配置
 *
 * @param freq_idx 频点，范围：0~200
 * @return int     0表示成功，非0表示失败
 */
int uc_wiota_set_freq_info(unsigned char freq_idx)
{
    char cmd_buf[100];

    if (200 < freq_idx)
    {
        TRACE_E("%s line %d input para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }

    TRACE_D("set_freq_idx %d", freq_idx);

    rt_sprintf(cmd_buf, "AT+WIOTAFREQ=%d", freq_idx);

    return uc_wiota_cmd_exec(cmd_buf);
}

/**
 * @brief 获取WIoTa工作频点
 *
 * @return unsigned char 0~200表示成功，0xFF表示失败
 */
unsigned char uc_wiota_get_freq_info(void)
{
    unsigned int freq_idx = 0xFF;

    uc_wiota_cmd_query("AT+WIOTAFREQ?", "+WIOTAFREQ:", "+WIOTAFREQ:%d", &freq_idx, 1);

    return freq_idx;
}

/**
 * @brief 设置WIoTa跳频频点，WIoTa支持简单的跳频，即在两个频点上工作
 *
 * @param hopping_freq 跳频频点，不要和工作频点一样，范围：0~200
 * @return int         0表示成功，非0表示失败
 */
int uc_wiota_set_hopping_freq(unsigned char hopping_freq)
{
    char cmd_buf[100];

    if (200 < hopping_freq)
    {
        TRACE_E("%s line %d input para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }

    TRACE_D("set_hopping_freq_idx %d", hopping_freq);

    rt_sprintf(cmd_buf, "AT+WIOTAHOPPING=%d,%d,0", HOPPING_SET_FREQ, hopping_freq);

    return uc_wiota_cmd_exec(cmd_buf);
}

/**
 * @brief 设置跳频模式接口，设置了跳频频点还需设置跳频模式，以指定在工作频点和跳频频点工作的帧数
 *
 * @param ori_freq_frame     在原频点（即工作频点）工作的帧数
 * @param hopping_freq_frame 在跳频频点工作的帧数
 * @return int               0表示成功，非0表示失败
 */
int uc_wiota_set_hopping_mode(unsigned char ori_freq_frame, unsigned char hopping_freq_frame)
{
    char cmd_buf[100];

    TRACE_D("set_hopping_freq_mode %d, %d", ori_freq_frame, hopping_freq_frame);

    rt_sprintf(cmd_buf, "AT+WIOTAHOPPING=%d,%d,%d", HOPPING_SET_MODE, ori_freq_frame, hopping_freq_frame);

    return uc_wiota_cmd_exec(cmd_buf);
}

/**
 * @brief 设置WIoTa最大的连接态终端数量，dlul_ratio为0时默认为72个，dlul_ratio为1时默认为144个，如果有超过该值的需求，在wiota_run之前调用设置
 *
 * @param max_iote_num 最大的连接态终端数量，最好为8的帧数倍，范围1~800
 * @return int         0表示成功，非0表示失败
 */
int uc_wiota_set_max_num_of_active_iote(unsigned short max_iote_num)
{
    char cmd_buf[100];

    if (0 == max_iote_num || 800 < max_iote_num)
    {
        TRACE_E("%s line %d input para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }

    TRACE_D("set_max_iote_num %d", max_iote_num);

    rt_sprintf(cmd_buf, "AT+WIOTAIOTENUM=%d", max_iote_num);

    return uc_wiota_cmd_exec(cmd_buf);
}

/**
 * @brief 获取WIoTa最大的连接态终端数量
 *
 * @return unsigned short 最大的连接态终端数量，0表示查询失败
 */
unsigned short uc_wiota_get_max_num_of_active_iote(void)
{
    unsigned int max_iote_num = 0;

    uc_wiota_cmd_query("AT+WIOTAIOTENUM?", "+WIOTAIOTENUM:", "+WIOTAIOTENUM:%d", &max_iote_num, 1);

    return max_iote_num;
}

/**
 * @brief 设置WIoTa速率模式和对应模式的值
 *        第一种模式，是设置下行单播速率，设置值为uc_mcs_level_e
 *        在第一种模式的基础上，在系统配置中dlul_ratio为1:2时，才能打开第二种模式，打开该模式能够提高该帧结构情况下两倍速率，默认第二种模式关闭
 *        在第一种模式的基础上，打开第三种模式，能够提升8*(1 << group_number)倍单终端的速率，但是会影响网络中其他终端的上行，建议在大数据量快速传输需求时使用
 *
 * @param rate_mode  WIoTa速率模式，对应枚举uc_data_rate_mode_e
 * @param rate_value normal模式取值为uc_mcs_level_e，且在不同symbol_length下支持的速率最大值亦有限制，symbol_length为0，1，2，3时对应的最大速率分别为
 *                   MCS4，MCS6，MCS7和MCS7
 *                   mid模式取值为0或1，表示开启或关闭中速模式，且只能在dlul_ratio为1:2时开启，1:1不支持
 *                   high模式取值为0或1~UC_DL_MAX_LEN，表示关闭或发送长度大于设定值时开启，且只支持dlul_ratio为1:1的配置，1:2不支持
 *                   crc_type表示基带crc的长度，默认为4字节，当crc_type为1时，表示1字节的crc，此种情况下单帧数据量会多三个字节，语音专用其他模式不用开启
 * @return int       0表示成功，非0表示失败
 */
int uc_wiota_set_data_rate(uc_data_rate_mode_e rate_mode, unsigned int rate_value)
{
    char cmd_buf[100];
    int result = RT_EOK;

    if (UC_RATE_NORMAL == rate_mode)
    {
        if (UC_MCS_LEVEL_AUTO < rate_value)
        {
            result = -RT_ERROR;
        }
    }
    else if (UC_RATE_MID == rate_mode || UC_RATE_CRC_TYPE == rate_mode)
    {
        if (0 != rate_value && 1 != rate_value)
        {
            result = -RT_ERROR;
        }
    }
    else if (UC_RATE_HIGH == rate_mode)
    {
        if (UC_DL_MAX_LEN < rate_value)
        {
            result = -RT_ERROR;
        }
    }
    else
    {
        result = -RT_ERROR;
    }

    if (result != RT_EOK)
    {
        TRACE_E("%s line %d input para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }

    TRACE_D("set_data_rate mode %d, value %d", rate_mode, rate_value);

    rt_sprintf(cmd_buf, "AT+WIOTARATE=%d,%d", rate_mode, rate_value);

    return uc_wiota_cmd_exec(cmd_buf);
}

/**
 * @brief 获取WIoTa速率模式的值
 *
 * @param rate_mode     WIoTa速率模式
 * @return unsigned int 参考设置接口的rate_value
 */
unsigned int uc_wiota_get_data_rate_value(uc_data_rate_mode_e rate_mode)
{
    char cmd_buf[100];
    unsigned int rate_value = 0;

    if (UC_RATE_HIGH < rate_mode)
    {
        TRACE_E("%s line %d input para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }

    rt_sprintf(cmd_buf, "AT+WIOTARATEQUERY=%d", rate_mode);

    uc_wiota_cmd_query(cmd_buf, "+WIOTARATEQUERY", "+WIOTARATEQUERY:%d", &rate_value, 1);

    return rate_value;
}

/**
 * @brief 设置WIoTa广播发送速率
 *
 * @param bc_mcs 广播发送速率，只能为固定MCS，不能为自动MCS，范围：MCS0~MCS6
 *               在不同symbol_length下支持的速率最大值亦有限制，symbol_length为0，1，2，3时对应的最大速率分别为MCS4，MCS6，MCS6和MCS5
 * @return int   0表示成功，非0表示失败
 */
int uc_wiota_set_broadcast_mcs(uc_mcs_level_e bc_mcs)
{
    char cmd_buf[100];

    if (UC_MCS_LEVEL_6 < bc_mcs)
    {
        TRACE_E("%s line %d input para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }

    TRACE_D("set_bc_mcs %d", bc_mcs);

    rt_sprintf(cmd_buf, "AT+WIOTABCMCS=%d", bc_mcs);

    return uc_wiota_cmd_exec(cmd_buf);
}

/**
 * @brief 获取WIoTa广播发送速率
 *
 * @return uc_mcs_level_e 广播发送速率
 */
uc_mcs_level_e uc_wiota_get_broadcast_mcs(void)
{
    unsigned int bc_mcs = UC_MCS_LEVEL_INVALID;

    uc_wiota_cmd_query("AT+WIOTABCMCS?", "+WIOTABCMCS:", "+WIOTABCMCS:%d", &bc_mcs, 1);

    return bc_mcs;
}

/**
 * @brief 设置WIoTa广播帧发送周期，默认11帧发送一次，广播帧用于发送一些控制信息和同步帧号，非特殊用途（如配合IoTE睡眠起来后发送数据）不建议修改
 *
 * @param bc_fn_cycle 广播帧发送周期，范围1~11，注意设置为1时，相当于每帧都发广播帧，会导致子帧6无法发送其他下行短消息
 * @return int        0表示成功，非0表示失败
 */
int uc_wiota_set_broadcast_fn_cycle(unsigned char bc_fn_cycle)
{
    char cmd_buf[100];

    if (11 < bc_fn_cycle)
    {
        TRACE_E("%s line %d input para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }

    TRACE_D("set_fn_cycle %d", bc_fn_cycle);

    rt_sprintf(cmd_buf, "AT+WIOTABCFNCYCLE=%d", bc_fn_cycle);

    return uc_wiota_cmd_exec(cmd_buf);
}

/**
 * @brief 获取WIoTa广播帧发送周期
 *
 * @return unsigned char 广播帧发送周期
 */
unsigned char uc_wiota_get_broadcast_fn_cycle(void)
{
    unsigned int bc_fn_cycle = 0;

    uc_wiota_cmd_query("AT+WIOTABCFNCYCLE?", "+WIOTABCFNCYCLE", "+WIOTABCFNCYCLE:%d", &bc_fn_cycle, 1);

    return bc_fn_cycle;
}

/**
 * @brief 设置广播发轮数，默认三轮，非特殊需求不建议更改
 *
 * @param round 发送轮数
 * @return int 0表示成功，非0表示失败
 */
int uc_wiota_set_broadcast_send_round(unsigned char round)
{
    char cmd_buf[100];

    if (0 == round)
    {
        TRACE_E("%s line %d input para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }

    TRACE_D("set_bc_send_round %d", round);

    rt_sprintf(cmd_buf, "AT+WIOTABCROUND=%d", round);

    return uc_wiota_cmd_exec(cmd_buf);
}

/**
 * @brief 获取广播发轮数
 *
 * @return unsigned char 发送轮数
 */
unsigned char uc_wiota_get_broadcast_send_round(void)
{
    unsigned int round = 0;

    uc_wiota_cmd_query("AT+WIOTABCROUND?", "+WIOTABCROUND", "+WIOTABCROUND:%d", &round, 1);

    return round;
}

/**
 * @brief 获取当前配置的WIoTa帧结构的单帧帧长
 *
 * @return unsigned int 帧长，单位：微妙
 */
unsigned int uc_wiota_get_frame_len(void)
{
    unsigned int frame_len = 0;

    uc_wiota_cmd_query("AT+WIOTAFRAMELEN?", "+WIOTAFRAMELEN", "+WIOTAFRAMELEN:%u", &frame_len, 1);

    return frame_len;
}

/**
 * @brief 获取AP当前帧号
 *
 * @return unsigned int 帧号
 */
unsigned int uc_wiota_get_frame_num(void)
{
    unsigned int frame_num = 0;

    uc_wiota_cmd_query("AT+WIOTAFRAMENUM?", "+WIOTAFRAMENUM", "+WIOTAFRAMENUM:%u", &frame_num, 1);

    return frame_num;
}

/**
 * @brief 获取AP8288运行状态
 *
 * @return int 0表示异常，1表示正常，其他表示获取失败
 */
int uc_wiota_get_ap8288_state(void)
{
    unsigned int state = 0;

    uc_wiota_cmd_query("AT+WIOTAAPSTATE?", "+WIOTAAPSTATE:", "+WIOTAAPSTATE:%d", &state, 1);

    return state;
}

/**
 * @brief 设置WIota发送时携带CRC校验，该接口设置的状态应该和IoTE同步，否则会造成数据接收错误，默认为发送长度大于100字节是添加CRC，该接口限制对所有下行
 *        生效（广播、OTA、组播和单播）
 *
 * @param crc_limit 为0表示关闭CRC，不管数据多长都不添加CRC；大于等于1时，表示发送长度大于设定值时添加CRC，如果要求所有长度数据都添加CRC，设置为1即可
 * @return int      0表示成功，非0表示失败
 */
int uc_wiota_set_crc(unsigned short crc_limit)
{
    char cmd_buf[100];

    if (UC_BC_MAX_LEN < crc_limit)
    {
        TRACE_E("%s line %d input para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }

    TRACE_D("set_crc_limit %d", crc_limit);

    rt_sprintf(cmd_buf, "AT+WIOTACRC=%d", crc_limit);

    return uc_wiota_cmd_exec(cmd_buf);
}

/**
 * @brief 获取当前的CRC限制值
 *
 * @return unsigned short 0~UC_BC_MAX_LEN表示成功，0xFFFF表示失败
 */
unsigned short uc_wiota_get_crc(void)
{
    unsigned int crc_limit = 0;

    uc_wiota_cmd_query("AT+WIOTACRC?", "+WIOTACRC:", "+WIOTACRC:%d", &crc_limit, 1);

    return crc_limit;
}

/**
 * @brief 获取黑名单并返回黑名单个数
 *
 * @param blacklist_num   黑名单个数
 * @return uc_blacklist_t 黑名单链表头，RT_NULL表示获取失败
 */
uc_blacklist_t *uc_wiota_get_blacklist(unsigned short *blacklist_num)
{
    struct at_response *resp = RT_NULL;
    int result = RT_EOK;
    unsigned int index = 0;
    unsigned int bl_num = 0;
    unsigned int user_id = 0;
    unsigned int bl_len = sizeof(uc_blacklist_t);

    RT_ASSERT(g_wiota_at_client);

    do
    {
        resp = at_create_resp(WIOTA_AT_RECV_BUFSZ, 0, rt_tick_from_millisecond(WIOTA_AT_RES_TIMEOUT));
        if (resp == RT_NULL)
        {
            TRACE_E("%s line %d create resp fail", __FUNCTION__, __LINE__);
            result = -RT_ENOMEM;
            break;
        }

        for (index = 0; index < WIOTA_AT_RETRY_CNT; index++)
        {
            if (at_obj_exec_cmd(g_wiota_at_client, resp, "AT+WIOTABLACKLIST?") == 0)
            {
                if (at_resp_parse_line_args_by_kw(resp, "+WIOTABLACKLIST:", "+WIOTABLACKLIST:%d", &bl_num) > 0)
                {
                    if (bl_num > 0)
                    {
                        unsigned int line_num = 0;

                        // line_counts的大小，表示接受到多少行数据，每一行就表示有一个黑名单，将黑名单添加到本地链表
                        for (line_num = 1; line_num <= resp->line_counts; line_num++)
                        {
                            if (at_resp_parse_line_args(resp, line_num, "+WIOTABLACKLIST:0x%x", &user_id) > 0)
                            {
                                uc_blacklist_t *new_node = (uc_blacklist_t *)rt_malloc(bl_len);
                                if (new_node == RT_NULL)
                                {
                                    TRACE_E("%s line %d malloc fail", __FUNCTION__, __LINE__);
                                    result = -RT_ENOMEM;
                                    break;
                                }
                                rt_memset(new_node, 0, bl_len);

                                new_node->user_id = user_id;

                                rt_slist_append(&g_blacklist.node, &new_node->node);
                            }
                        }
                    }
                    break;
                }
            }
        }
        if (index >= WIOTA_AT_RETRY_CNT)
        {
            TRACE_E("cmd AT+WIOTABLACKLIST? error.");
            result = -RT_ERROR;
            break;
        }
    } while (0);

    if (blacklist_num != RT_NULL)
    {
        *blacklist_num = bl_num;
    }

    at_delete_resp(resp);

    if (result != RT_EOK)
    {
        /* code */
        uc_wiota_blacklist_deinit();
        return RT_NULL;
    }
    else
    {
        return &g_blacklist;
    }
}

/**
 * @brief 添加一组user id到黑名单
 *
 * @param user_id     要添加的黑名单id数组首地址
 * @param user_id_num 要添加的黑名单id个数
 * @return int        0表示成功，非0表示失败
 */
int uc_wiota_add_iote_to_blacklist(unsigned int *user_id, unsigned short user_id_num)
{
    int result = RT_EOK;
    unsigned short user_id_index = 0;
    char cmd_buf[100];

    if (RT_NULL == user_id || 0 == user_id_num)
    {
        TRACE_E("%s line %d para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }

    for (user_id_index = 0; user_id_index < user_id_num; user_id_index++)
    {
        rt_sprintf(cmd_buf, "AT+WIOTABLACKLIST=%x,%d", user_id[user_id_index], ADD_BLACKLIST);
        result &= uc_wiota_cmd_exec(cmd_buf);
        if (RT_EOK != result)
        {
            continue;
        }
    }

    return result;
}

/**
 * @brief 将一组user id从黑名单中移除
 *
 * @param user_id     要移除的黑名单id数组首地址
 * @param user_id_num 要移除的黑名单id个数
 * @return int        0表示成功，非0表示失败
 */
int uc_wiota_remove_iote_from_blacklist(unsigned int *user_id, unsigned short user_id_num)
{
    int result = RT_EOK;
    unsigned short user_id_index = 0;
    char cmd_buf[100];

    if (user_id == RT_NULL || user_id_num == 0)
    {
        TRACE_E("%s line %d para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }

    for (user_id_index = 0; user_id_index < user_id_num; user_id_index++)
    {
        rt_sprintf(cmd_buf, "AT+WIOTABLACKLIST=%x,%d", user_id[user_id_index], REMOVE_BLACKLIST);
        result &= uc_wiota_cmd_exec(cmd_buf);
        if (RT_EOK != result)
        {
            continue;
        }
    }

    return result;
}

/**
 * @brief WIoTa协议栈log开关，默认开启串口log，SPI log需要Trace log工具配合才能抓取
 *
 * @param log_type WIoTa协议栈log类型，串口log或SPI log
 * @param is_open  1表示开启log，0表示关闭log
 * @return int     0表示成功，非0表示失败
 */
int uc_wiota_log_switch(uc_log_type_e log_type, unsigned char is_open)
{
    char cmd_buf[100];
    int log_mode = 0;

    if (log_type == UC_LOG_UART)
    {
        log_mode = is_open ? LOG_OPEN : LOG_CLOSE;
    }
    else if (log_type == UC_LOG_SPI)
    {
        log_mode = is_open ? LOG_SPI_OPEN : LOG_SPI_CLOSE;
    }
    else
    {
        TRACE_E("%s line %d input para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }

    rt_sprintf(cmd_buf, "AT+WIOTALOG=%d", log_mode);

    return uc_wiota_cmd_exec(cmd_buf);
}

/**
 * @brief WIoTa发送下行消息处理函数
 *
 * @param cmd           发送AT指令
 * @param send_data     发送数据指针
 * @param send_data_len 发送数据长度
 * @param timeout       发送超时时间
 * @param callback      发送结果回调函数指针
 * @param send_type     发送类型，广播、组播或单播
 * @return uc_result_e  发送结果，成功、超时或失败
 */
static uc_result_e uc_wiota_handle_send_msg(char *cmd,
                                            unsigned char *send_data,
                                            unsigned short send_data_len,
                                            signed int timeout,
                                            uc_send_callback callback,
                                            send_type_e send_type)
{
    unsigned int event = 0;
    int result = RT_EOK, event_result = 0;
    at_response_t resp = RT_NULL;

    RT_ASSERT(g_wiota_at_client);
    // 静态函数，调用前已经做过参数检查了，这里不在做检查

    do
    {
        // 创建发送AT的接收结构体指针，timeout时间要略大于AP等待输入的时间（10s）
        resp = at_create_resp(AT_CMD_MAX_LEN, 1, rt_tick_from_millisecond(11 * 1000));
        if (resp == RT_NULL)
        {
            TRACE_E("%s line %d create resp fail", __FUNCTION__, __LINE__);
            result = -RT_ENOMEM;
            break;
        }

        // callback不为空时为非阻塞调用；为空时为阻塞调用，需等待发送结果返回后才能继续往下执行
        if (callback != RT_NULL)
        {
            g_send_cb[send_type] = callback;
        }
        else
        {
            // 阻塞调用，设置期望接收的事件类型
            event = WIOTA_EVENT_SEND_OK | WIOTA_EVENT_SEND_FAIL | WIOTA_EVENT_SEND_TIMEOUT;
            uc_wiota_event_recv(event, 0, RT_EVENT_FLAG_OR);
        }

        /* 数据发送AT分为两部分，第一部分为AT指令，第二部分为不定长数据，此种AT需要设置结束符，一般设为">"，AT server在收到AT指令后，会输出一个">"，
           表示等待接收不定长数据，AT client收到">"后发送不定长数据，发送完成后将结束符重置为0
        */
        // 1.设置结束符为"<"
        at_obj_set_end_sign(g_wiota_at_client, '>');

        // 2.执行cmd，告诉AT server要接收多长的数据，并等待接收
        if (at_obj_exec_cmd(g_wiota_at_client, resp, cmd) < 0)
        {
            TRACE_E("%s line %d at_obj_exec_cmd fail", __FUNCTION__, __LINE__);
            result = -RT_ERROR;
            break;
        }

        // 3.AT client收到"<"后，发送数据
        result = (int)at_client_obj_send(g_wiota_at_client, (const char *)send_data, send_data_len);
        if (result == 0)
        {
            TRACE_E("%s line %d at_client_obj_send fail", __FUNCTION__, __LINE__);
            result = -RT_ERROR;
            break;
        }

        // 4.阻塞调用，等待发送结果
        if (callback == RT_NULL)
        {
            /* waiting OK or failed result */
            event_result = uc_wiota_event_recv(event, rt_tick_from_millisecond(timeout + 100), RT_EVENT_FLAG_OR);
            if (event_result < 0)
            {
                TRACE_E("%s line %d uc_wiota_event_recv timeout", __FUNCTION__, __LINE__);
                result = -RT_ETIMEOUT;
                break;
            }
            /* check result */
            if (event_result & WIOTA_EVENT_SEND_FAIL)
            {
                TRACE_E("wiota ap send failed");
                result = -RT_ERROR;
                break;
            }
        }
        else
        {
            // 4.非阻塞调用，继续往下执行，发送结果将有回调通知
            result = RT_EOK;
            break;
        }
    } while (0);

    // 5.完成后重置结束标识为0
    at_obj_set_end_sign(g_wiota_at_client, 0);
    // 释放发送AT的接收结构体指针
    at_delete_resp(resp);

    if (result >= 0)
    {
        return UC_OP_SUCC;
    }
    else
    {
        return UC_OP_FAIL;
    }
}

/**
 * @brief 发送失败处理函数
 *
 * @param user_id  单播时为IoTE的user id，广播时或OTA则为uc_bc_mode_e，组播时则为组播id
 * @param data_id  用户传入的para
 * @param callback 发送结果回调函数指针
 */
static void uc_wiota_report_send_fail_by_cb(unsigned int user_id, unsigned int data_id, uc_send_callback callback)
{
    if (callback != NULL)
    {
        uc_send_recv_t result = {0};

        result.user_id = user_id;
        result.data_id = data_id;
        result.result = UC_OP_FAIL;
        callback(&result);
    }
}

/**
 * @brief WIoTa广播（普通广播和OTA）发送接口
 *
 * @param send_data     要发送的广播数据指针
 * @param send_data_len 要发送的广播数据长度
 * @param mode          广播模式，普通广播或OTA
 * @param timeout       广播发送超时时间，该时间跟帧结构配置、数据量和广播速率相关，如果不好把控，建议往大了设
 * @param callback      发送结果回调，传入RT_NULL时表示阻塞发送，非RT_NULL时表示非阻塞发送
 * @param para          用户自定义参数，该参数会在发送结果回调时传回来，一般传入发送数据的地址，用于知晓每段数据的发送结果
 * @return uc_result_e  成功或超时，广播没有ACK理论上不会有失败
 */
uc_result_e uc_wiota_send_broadcast_data(unsigned char *send_data,
                                         unsigned short send_data_len,
                                         uc_bc_mode_e mode,
                                         signed int timeout,
                                         uc_send_callback callback,
                                         void *para)
{
    int result = RT_EOK;
    unsigned int data_id = (unsigned int)para;
    unsigned int is_block = (callback == RT_NULL ? 1 : 0);
    char cmd_buf[100] = {0};

    if (send_data == RT_NULL || 0 == send_data_len || UC_BC_MAX_LEN < send_data_len || OTA_BROADCAST < mode)
    {
        TRACE_E("%s line %d input param error", __FUNCTION__, __LINE__);
        uc_wiota_report_send_fail_by_cb(mode, data_id, callback);
        return UC_OP_FAIL;
    }

    rt_sprintf(cmd_buf, "AT+WIOTABC=%x,%d,%d,%d,%d", data_id, send_data_len, mode, timeout, is_block);

    result = uc_wiota_handle_send_msg(cmd_buf, send_data, send_data_len, timeout, callback, SEND_TYPE_BC);
    if (UC_OP_SUCC != result)
    {
        uc_wiota_report_send_fail_by_cb(mode, data_id, callback);
    }

    return result;
}

/**
 * @brief 设置WIoTa组播id，最多支持设置8个组播id，该id要和IoTE设置的组播接收id一致
 *
 * @param multicast_id 要设置的一组组播id
 * @param id_num       要设置的组播id个数
 * @return int         0表示成功，非0表示失败
 */
int uc_wiota_set_multicast_id(unsigned int *multicast_id, unsigned int id_num)
{
    int result = RT_EOK;
    char cmd_buf[100] = {0};
    unsigned int id[8] = {0};

    if (multicast_id == NULL || id_num > 8)
    {
        TRACE_E("%s line %d para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }

    rt_memset(id, 0, sizeof(id));
    for (int i = 0; i < id_num; i++)
    {
        id[i] = multicast_id[i];
    }

    // 单次最多添加四个，超过四个，设置两次
    rt_sprintf(cmd_buf, "AT+WIOTAMCID=0,%x,%x,%x,%x", id[0], id[1], id[2], id[3]);

    result = uc_wiota_cmd_exec(cmd_buf);

    if (id_num > 4)
    {
        rt_sprintf(cmd_buf, "AT+WIOTAMCID=0,%x,%x,%x,%x", id[4], id[5], id[6], id[7]);

        result &= uc_wiota_cmd_exec(cmd_buf);
    }

    return result;
}

/**
 * @brief 删除一组WIoTa组播id
 *
 * @param multicast_id 要删除的一组组播id首地址
 * @param id_num       要删除的组播id个数
 * @return int         0表示成功，非0表示失败
 */
int uc_wiota_del_multicast_id(unsigned int *multicast_id, unsigned int id_num)
{
    int result = RT_EOK;
    char cmd_buf[100] = {0};
    unsigned int id[8] = {0};

    if (multicast_id == NULL || id_num > 8)
    {
        TRACE_E("%s line %d para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }

    rt_memset(id, 0, sizeof(id));
    for (int i = 0; i < id_num; i++)
    {
        id[i] = multicast_id[i];
    }

    // 单次最多删除四个，超过四个，删除两次
    rt_sprintf(cmd_buf, "AT+WIOTAMCID=1,%x,%x,%x,%x", id[0], id[1], id[2], id[3]);

    result = uc_wiota_cmd_exec(cmd_buf);

    if (id_num > 4)
    {
        rt_sprintf(cmd_buf, "AT+WIOTAMCID=1,%x,%x,%x,%x", id[4], id[5], id[6], id[7]);

        result &= uc_wiota_cmd_exec(cmd_buf);
    }

    return result;
}

/**
 * @brief WIoTa组播发送接口
 *
 * @param send_data     要发送的组播数据指针
 * @param send_data_len 要发送的组播数据长度
 * @param multicast_id  组播id，该id必须先通过设置组播id接口设置，否则会返回失败
 * @param timeout       组播发送超时时间，该时间跟帧结构配置、业务类型、数据量和广播速率相关，如果不好把控，建议往大了设
 * @param callback      发送结果回调，传入RT_NULL时表示阻塞发送，非RT_NULL时表示非阻塞发送
 * @param para          用户自定义参数，该参数会在发送结果回调时传回来，一般传入发送数据的地址，用于知晓每段数据的发送结果
 * @return uc_result_e  成功或超时，组播没有ACK理论上不会有失败，除非组播id未先设置
 */
uc_result_e uc_wiota_send_multicast_data(unsigned char *send_data,
                                         unsigned short send_data_len,
                                         unsigned int multicast_id,
                                         signed int timeout,
                                         uc_send_callback callback,
                                         void *para)
{
    int result = RT_EOK;
    unsigned int data_id = (unsigned int)para;
    unsigned int is_block = (callback == RT_NULL ? 1 : 0);
    char cmd_buf[100] = {0};

    if (RT_NULL == send_data || 0 == send_data_len || UC_BC_MAX_LEN < send_data_len || 0 == multicast_id)
    {
        TRACE_E("%s line %d input param error", __FUNCTION__, __LINE__);
        uc_wiota_report_send_fail_by_cb(multicast_id, data_id, callback);
        return UC_OP_FAIL;
    }

    rt_sprintf(cmd_buf, "AT+WIOTAMC=%x,%d,%x,%d,%d", data_id, send_data_len, multicast_id, timeout, is_block);

    result = uc_wiota_handle_send_msg(cmd_buf, send_data, send_data_len, timeout, callback, SEND_TYPE_MC);
    if (UC_OP_SUCC != result)
    {
        uc_wiota_report_send_fail_by_cb(multicast_id, data_id, callback);
    }

    return result;
}

/**
 * @brief WIoTa单播发送接口
 *
 * @param send_data     要发送的单播数据指针
 * @param send_data_len 要发送的但播数据长度
 * @param user_id       要发送给某个IoTE的user id，该IoTE必须先和AP保持同步状态，否则会发送失败
 * @param timeout       单播发送超时时间，该时间跟帧结构配置、业务类型、数据量和广播速率相关，如果不好把控，建议往大了设
 * @param callback      发送结果回调，传入RT_NULL时表示阻塞发送，非RT_NULL时表示非阻塞发送
 * @param para          用户自定义参数，该参数会在发送结果回调时传回来，一般传入发送数据的地址，用于知晓每段数据的发送结果
 * @return uc_result_e  成功、超时或失败
 */
uc_result_e uc_wiota_send_data(unsigned char *send_data,
                               unsigned short send_data_len,
                               unsigned int user_id,
                               signed int timeout,
                               uc_send_callback callback,
                               void *para)
{
    int result = RT_EOK;
    unsigned int data_id = (unsigned int)para;
    unsigned int is_block = (callback == RT_NULL ? 1 : 0);
    char cmd_buf[100] = {0};

    if (RT_NULL == send_data || 0 == send_data_len || UC_DL_MAX_LEN < send_data_len || 0 == user_id)
    {
        TRACE_E("%s line %d input param error", __FUNCTION__, __LINE__);
        uc_wiota_report_send_fail_by_cb(user_id, data_id, callback);
        return UC_OP_FAIL;
    }

    rt_sprintf(cmd_buf, "AT+WIOTASEND=%x,%d,%x,%d,%d", data_id, send_data_len, user_id, timeout, is_block);

    result = uc_wiota_handle_send_msg(cmd_buf, send_data, send_data_len, timeout, callback, SEND_TYPE_UC);
    if (UC_OP_SUCC != result)
    {
        uc_wiota_report_send_fail_by_cb(user_id, data_id, callback);
    }

    return result;
}

/**
 * @brief WIoTa顺序单播业务发送接口
 *
 * @param send_data      要发送的单播数据指针
 * @param send_data_len  要发送的但播数据长度
 * @param user_id        要发送给某个IoTE的user id，该IoTE必须先和AP保持同步状态，否则会发送失败
 * @param timeout        单播发送超时时间，该时间跟帧结构配置、业务类型、数据量和广播速率相关，如果不好把控，建议往大了设
 * @param order_business 顺序业务标识，为0时和普通单播业务相同，为1时表示该业务为顺序业务，会等待之前的业务终端掉线后再发送
 * @param callback       发送结果回调，传入RT_NULL时表示阻塞发送，非RT_NULL时表示非阻塞发送
 * @param para           用户自定义参数，该参数会在发送结果回调时传回来，一般传入发送数据的地址，用于知晓每段数据的发送结果
 * @return uc_result_e   成功、超时或失败
 */
uc_result_e uc_wiota_send_data_order(unsigned char *send_data,
                                     unsigned short send_data_len,
                                     unsigned int user_id,
                                     signed int timeout,
                                     unsigned int order_business,
                                     uc_send_callback callback,
                                     void *para)
{
    int result = RT_EOK;
    unsigned int data_id = (unsigned int)para;
    unsigned int is_block = (callback == RT_NULL ? 1 : 0);
    char cmd_buf[100] = {0};

    if (RT_NULL == send_data || 0 == send_data_len || UC_DL_MAX_LEN < send_data_len || 0 == user_id)
    {
        TRACE_E("%s line %d input param error", __FUNCTION__, __LINE__);
        uc_wiota_report_send_fail_by_cb(user_id, data_id, callback);
        return UC_OP_FAIL;
    }

    rt_sprintf(cmd_buf, "AT+WIOTASENDORDER=%x,%d,%x,%d,%d,%d", data_id, send_data_len, user_id, timeout, order_business, is_block);

    result = uc_wiota_handle_send_msg(cmd_buf, send_data, send_data_len, timeout, callback, SEND_TYPE_UC);
    if (UC_OP_SUCC != result)
    {
        uc_wiota_report_send_fail_by_cb(user_id, data_id, callback);
    }

    return result;
}

/**
 * @brief 非阻塞调用失败后返回结果
 *
 * @param callback 扫频回调
 */
static void uc_wiota_report_scan_fail_by_cb(uc_scan_callback callback)
{
    if (callback != NULL)
    {
        uc_scan_recv_t result = {0};

        result.result = UC_OP_FAIL;
        callback(&result);
    }
}

/**
 * @brief WIoTa频点扫描接口，用于扫描一组频点，返回每个频点的频点信息
 *
 * @param freq           要扫描的一组频点数组的首地址
 * @param freq_nu        要扫描的一组频点个数
 * @param scan_type      扫频类型，0为普通扫描，执行慢，但结果更准确；1为快速扫描，执行快，但结果相对较差
 * @param timeout        扫频超时，建议1min以上
 * @param callback       扫频结果回调，传入RT_NULL时表示阻塞调用，非RT_NULL时表示非阻塞调用，一般都用阻塞方式
 * @param scan_result    扫频结果，包含了每个频点的信息
 * @return uc_result_e   成功、超时或失败
 */
uc_result_e uc_wiota_scan_freq(unsigned char *freq,
                               unsigned char freq_num,
                               unsigned char scan_type,
                               signed int timeout,
                               uc_scan_callback callback,
                               uc_scan_recv_t *scan_result)
{
    struct at_response *resp = RT_NULL;
    int result = RT_EOK;
    unsigned int index = 0;
    int freq_string_len = 0;
    unsigned char *freq_string = RT_NULL;
    uc_scan_freq_t *recv_buf = RT_NULL;
    int recv_buf_len = 0;
    scan_data_t *scan_data;

    RT_ASSERT(g_wiota_at_client);

    if ((0 != scan_type && 1 != scan_type) || RT_NULL == scan_result)
    {
        TRACE_E("%s line %d input para error", __FUNCTION__, __LINE__);
        uc_wiota_report_scan_fail_by_cb(callback);
        return UC_OP_FAIL;
    }

    do
    {
        // 全扫
        if ((freq == RT_NULL) || (freq_num == 0))
        {
            resp = at_create_resp(1024, 1, rt_tick_from_millisecond(WIOTA_SCAN_FREQ_TIMEOUT));
            if (resp == RT_NULL)
            {
                TRACE_E("%s line %d create resp fail", __FUNCTION__, __LINE__);
                result = -RT_ENOMEM;
                break;
            }

            for (index = 0; index < WIOTA_AT_RETRY_CNT; index++)
            {
                if (at_obj_exec_cmd(g_wiota_at_client, resp, "AT+WIOTASCANFREQ=0,0,0,%d,1", scan_type) == 0)
                {
                    break;
                }
            }
            if (index >= WIOTA_AT_RETRY_CNT)
            {
                TRACE_E("cmd AT+WIOTASCANFREQ error.");
                result = -RT_ERROR;
                break;
            }
        }
        else
        {
            // 扫描一组频点，与发送数据AT类似，前提
            unsigned short max_str_len = freq_num * 5;
            freq_string = rt_malloc(max_str_len);
            if (freq_string == RT_NULL)
            {
                TRACE_E("%s line %d malloc fail", __FUNCTION__, __LINE__);
                result = -RT_ENOMEM;
                break;
            }
            rt_memset(freq_string, 0, max_str_len);
            // 将频点数组转化为字符串
            uc_array_to_string(freq, freq_num, freq_string);
            freq_string_len = rt_strlen((char *)freq_string);

            // 创建扫频结果接收结构体指针
            resp = at_create_resp(1024, 1, rt_tick_from_millisecond(WIOTA_AT_RES_TIMEOUT));
            if (resp == RT_NULL)
            {
                TRACE_E("%s line %d create resp fail", __FUNCTION__, __LINE__);
                result = -RT_ENOMEM;
                break;
            }

            // 设置结束符为"<"
            at_obj_set_end_sign(g_wiota_at_client, '>');

            // 执行扫频指令
            if (at_obj_exec_cmd(g_wiota_at_client, resp, "AT+WIOTASCANFREQ=%d,%d,%d,%d,1", timeout, freq_string_len + 2, freq_num, scan_type) < 0)
            {
                TRACE_E("%s line %d at_obj_exec_cmd fail", __FUNCTION__, __LINE__);
                result = -RT_ERROR;
                break;
            }

            // 将转换后的频点字符串发生给AP
            result = (int)at_client_obj_send(g_wiota_at_client, (char *)freq_string, freq_string_len + 2);

            rt_free(freq_string);
            freq_string = RT_NULL;

            if (result <= 0)
            {
                TRACE_E("%s line %d at_client_obj_send fail", __FUNCTION__, __LINE__);
                result = -RT_ERROR;
                break;
            }
        }

        // 阻塞调用，等待接收扫频结果
        if (callback == NULL)
        {
            if (RT_EOK == manager_recv_queue(g_wiota_scan_mq, (void **)&scan_data, timeout))
            {
                // 接收成功，处理扫频结果
                if (scan_data->data != RT_NULL && scan_data->num > 0)
                {
                    recv_buf_len = sizeof(uc_scan_freq_t) * scan_data->num;
                    recv_buf = (uc_scan_freq_t *)rt_malloc(recv_buf_len);
                    if (recv_buf == RT_NULL)
                    {
                        TRACE_E("%s line %d malloc fail len %d", __FUNCTION__, __LINE__, recv_buf_len);
                        result = -RT_ENOMEM;
                        break;
                    }
                    rt_memset(recv_buf, 0, recv_buf_len);

                    uc_string_to_array(scan_data->data, (unsigned char *)recv_buf);

                    scan_result->freq_info = recv_buf;
                    rt_free(scan_data->data);
                    scan_data->data = RT_NULL;
                    result = RT_EOK;
                }
                else
                {
                    result = -RT_ERROR;
                }
                scan_result->freq_num = scan_data->num;
                scan_result->result = scan_data->result;

                rt_free(scan_data);
                scan_data = RT_NULL;
            }
            else
            {
                TRACE_E("%s line %d manager_recv_queue fail", __FUNCTION__, __LINE__);
                result = -RT_ERROR;
                break;
            }
            break;
        }
        else
        {
            g_scan_cb = callback;
            result = RT_EOK;
            break;
        }
    } while (0);

    // 完成后重置结束标识为0
    at_obj_set_end_sign(g_wiota_at_client, 0);
    // 释放发送AT的接收结构体指针
    at_delete_resp(resp);

    if (result != RT_EOK)
    {
        /* code */
        uc_wiota_report_scan_fail_by_cb(callback);
        return UC_OP_FAIL;
    }
    else
    {
        /* code */
        return UC_OP_SUCC;
    }
}

/**
 * @brief 读取AP模组上AP8288的芯片温度（即基带温度），只有在没有上下行任务时才能读取，否则会直接返回失败
 *
 * @param callback  温度查询回调函数，传入RT_NULL时表示阻塞调用，非RT_NULL时表示非阻塞调用
 * @param read_temp 读取到的温度结果
 * @param timeout   读取超时时间
 * @return int      0表示成功，非0表示失败
 */
int uc_wiota_read_temperature(uc_temp_callback callback, uc_temp_recv_t *read_temp, signed int timeout)
{
    int result = RT_EOK;
    unsigned int temp = 0;

    if (RT_NULL == read_temp)
    {
        TRACE_E("%s line %d input para error", __FUNCTION__, __LINE__);
        return UC_OP_FAIL;
    }

    result = uc_wiota_cmd_query("AT+WIOTATEMP?", "+WIOTATEMP:", "+WIOTATEMP:%d", &temp, 1);
    if (result == RT_EOK)
    {
        read_temp->result = UC_OP_SUCC;
        read_temp->temp = temp;
    }

    return result;
}

/**
 * @brief 通过user id生成扰码的算法
 *
 * @param id            user id
 * @param scb0          扰码0
 * @param scb1          扰码1
 * @return unsigned int user id对应的扰码
 */
static unsigned int uc_query_scrambleid_by_userid(unsigned int id, unsigned int scb0, unsigned int scb1)
{
    unsigned int scramble_id = 0;
    unsigned int hi, lo, b0, b1;

    /* x0 = ((id[31] shr 1) xor r1)[31:0] */
    unsigned int x0 = (((id & 0x80000000) >> 1) ^ scb1) & 0x7FFFFFFF;
    /* x1 = (id xor r0)[31:0] */
    unsigned int x1 = (id ^ scb0) & 0x7FFFFFFF;
    /* length = (id[7:0] + (id[23:16] shr 16))[7:0] + 20 */
    int length = (((id & 0x7F) + ((id & 0x7F0000) >> 16)) & 0x7F) + 20;

    for (int i = 0; i < length; ++i)
    {
        /* loop xor on x0 */
        /* ------------------------------------------- */
        /*   30  29  28  27  ...  03 | 02  01  00  31' */
        /* ^ 27  26  25  24  ...  00 | 31' 30' 29' 28' */
        /* ------------------------------------------- */
        /*   31' 30' 29' 28' ...  04'| 03' 02' 01' 00' */
        /*  |<----------hi---------->|<------lo----->| */
        hi = (x0 & 0x7FFFFFF8) << 1;
        hi ^= (x0 & 0x0FFFFFFF) << 4;
        lo = ((x0 & 0x7) << 1) | ((hi >> 31) & 0x1);
        lo ^= ((x0 & 0x0) << 4) | ((hi >> 28) & 0xF);
        x0 = hi | lo;
        /* loop xor on x1 */
        /* ------------------------------------------- */
        /*   30  29  28  27  ...  03 | 02  01  00  31' */
        /*   29  28  27  26  ...  02 | 01  00  31' 30' */
        /*   28  27  26  25  ...  01 | 00  31' 30' 29' */
        /* ^ 27  26  25  24  ...  00 | 31' 30' 29' 28' */
        /* ------------------------------------------- */
        /*   31' 30' 29' 28' ...  04'| 03' 02' 01' 00' */
        /*  |<----------hi---------->|<------lo----->| */
        hi = (x1 & 0x7FFFFFF8) << 1;
        hi ^= (x1 & 0x3FFFFFFC) << 2;
        hi ^= (x1 & 0x1FFFFFFE) << 3;
        hi ^= (x1 & 0x0FFFFFFF) << 4;
        lo = ((x1 & 0x7) << 1) | ((hi >> 31) & 0x1);
        lo ^= ((x1 & 0x3) << 2) | ((hi >> 30) & 0x3);
        lo ^= ((x1 & 0x1) << 3) | ((hi >> 29) & 0x7);
        lo ^= ((x1 & 0x0) << 4) | ((hi >> 28) & 0xF);
        x1 = hi | lo;
    }

    /* b0 = x0[30] ^ x0[27] */
    b0 = ((x0 >> 30) ^ (x0 >> 27)) & 0x1;
    /* x0 = (x0[31:0] shl 1) + b0 */
    x0 = (x0 << 1) | b0;
    /* b1 = x1[30] ^ x1[29] ^ x1[28] ^ x1[27] */
    b1 = ((x1 >> 30) ^ (x1 >> 29) ^ (x1 >> 28) ^ (x1 >> 27)) & 0x1;
    /* x1 = (x1[31:0] shl 1) + b1 */
    x1 = (x1 << 1) | b1;

    scramble_id = x0 ^ x1;

    return scramble_id;
}

/**
 * @brief 查询user id在帧结构的位置，id管理实现IoTE位置平铺的关键函数
 *
 * @param user_id        要查询的一组user id数组的首地址
 * @param user_id_num    要查询的一组user id个数
 * @return uc_dev_pos_t* 位置信息结构体指针，需要调用者手动释放
 */
uc_dev_pos_t *uc_wiota_query_dev_pos_by_userid(unsigned int *user_id, unsigned int user_id_num)
{
    uc_dev_pos_t *dev_pos = RT_NULL;
    unsigned int scramble_id = 0;
    unsigned int scb0 = 0;
    unsigned int scb1 = 0;
    unsigned char ul_group_num = 0;
    unsigned char group_idx = 0, burst_idx = 0, slot_idx = 0;
    unsigned int pos_num = 0;
    sub_system_config_t config = {0};

    if (RT_NULL == user_id || 0 == user_id_num)
    {
        TRACE_E("%s line %d parameter error", __FUNCTION__, __LINE__);
        return RT_NULL;
    }

    if (RT_EOK != uc_wiota_get_system_config(&config))
    {
        TRACE_E("pos query get subsystem config fail");
        return RT_NULL;
    }
    ul_group_num = 1 << config.group_number;
    uc_algo_srand(config.subsystem_id);
    scb0 = uc_algo_rand_u32();
    scb1 = uc_algo_rand_u32();
    TRACE_I("subsystem_id 0x%x scb0 0x%x, scb1 0x%x, id_num %d",
            config.subsystem_id, scb0, scb1, user_id_num);

    dev_pos = (uc_dev_pos_t *)rt_malloc(sizeof(uc_dev_pos_t) * user_id_num);
    RT_ASSERT(dev_pos);
    rt_memset(dev_pos, 0, sizeof(uc_dev_pos_t) * user_id_num);

    for (int i = 0; i < user_id_num; i++)
    {
        scramble_id = uc_query_scrambleid_by_userid(user_id[i], scb0, scb1);

        scramble_u scramble = {data : scramble_id};
        group_idx = scramble.bit_info.group_idx & (ul_group_num - 1);
        burst_idx = scramble.bit_info.burst_idx;
        slot_idx = scramble.bit_info.slot_idx;

        dev_pos[pos_num].group_idx = group_idx;
        dev_pos[pos_num].burst_idx = burst_idx;
        dev_pos[pos_num].slot_idx = slot_idx;
        pos_num++;
        // TRACE_I("user_id 0x%8x scb 0x%8x pos %d %d %d",
        //            user_id[i], scramble_id, dev_pos[i].group_idx, dev_pos[i].burst_idx, dev_pos[i].slot_idx);
    }

    return dev_pos;
}

/**
 * @brief 注册授时信息回调函数，当授时状态发生变化时，触发回调，将状态传给应用层
 *
 * @param callback
 */
void uc_wiota_register_time_service_state_callback(uc_time_service_callback callback)
{
    g_ts_cb = callback;
    if (callback)
    {
        g_ts_info_cb = RT_NULL;
    }
}

/**
 * @brief 注册授时信息回调函数，当授时状态发生变化时，触发回调，将状态传给应用层，上报信息更多
 *
 * @param callback
 */
void uc_wiota_register_time_service_info_callback(uc_time_service_info_callback callback)
{
    g_ts_info_cb = callback;
    if (callback)
    {
        g_ts_cb = RT_NULL;
    }
    uc_wiota_cmd_exec("AT+WIOTAREGNEWCB=0,1");
}

/**
 * @brief 开启或关闭WIoTa帧边界校准功能，默认关闭，前提是必须开启一种授时方式
 *
 * @param is_open 开关标志，0表示关闭，1表示开启
 * @return int    0表示成功，非0表示失败
 */
int uc_wiota_set_frame_boundary_align_func(unsigned char is_open)
{
    char cmd_buf[100];

    TRACE_D("set_fb_func %d", is_open);

    rt_sprintf(cmd_buf, "AT+WIOTAFBALIGN=%d", is_open);

    return uc_wiota_cmd_exec(cmd_buf);
}

/**
 * @brief 根据需求开启或关闭一种授时模式，只可同时开启一种模式，最新的网关将采用同步助手的模式
 *
 * @param type    授时模式time_service_type_e
 * @param is_open 开关标识，0表示关闭，1表示开启
 * @return int    0表示成功，非0表示失败
 */
int uc_wiota_set_time_service_func(time_service_type_e type, unsigned char is_open)
{
    char cmd_buf[100];

    TRACE_D("set_ts_func type %d, is_open %d", type, is_open);

    rt_sprintf(cmd_buf, "AT+WIOTATSMODE=%d,%d", type, is_open);

    return uc_wiota_cmd_exec(cmd_buf);
}

/**
 * @brief 设置授时周期，默认15分钟授时一次，范围5~60分钟
 *
 * @param cycle_min 授时周期，单位分钟
 * @return int      0表示成功，非0表示失败
 */
int uc_wiota_set_time_service_cycle(unsigned char cycle_min)
{
    char cmd_buf[100];

    TRACE_D("set_ts_cycle cycle_min %d", cycle_min);

    rt_sprintf(cmd_buf, "AT+WIOTATSCYCLE=%d", cycle_min);

    return uc_wiota_cmd_exec(cmd_buf);
}

/**
 * @brief 获取授时模式开关状态
 *
 * @param func_gnss GPS授时模式开关状态
 * @param func_1588 1588协议授时模式开关状态
 * @param func_sync 同步助手授时模式开关状态
 * @return int      0表示成功，非0表示失败
 */
int uc_wiota_get_time_service_func(unsigned char *func_gnss, unsigned char *func_1588, unsigned char *func_sync)
{
    int result = RT_EOK;
    unsigned int out_value[3] = {0};

    if (RT_NULL == func_gnss || RT_NULL == func_1588 || RT_NULL == func_sync)
    {
        TRACE_E("%s line %d para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }

    result = uc_wiota_cmd_query("AT+WIOTATSMODE?", "+WIOTATSMODE:", "+WIOTATSMODE:%d,%d,%d", out_value, 3);
    if (result == RT_EOK)
    {
        *func_gnss = out_value[0];
        *func_1588 = out_value[1];
        *func_sync = out_value[2];
    }

    return result;
}

/**
 * @brief 启动同步授时，需要设置一种授时方式
 *
 * @return int    0表示成功，非0表示失败
 */
int uc_wiota_time_service_start(void)
{
    return uc_wiota_cmd_exec("AT+WIOTATSRUN=1");
}

/**
 * @brief 停止同步授时，一般情况下，开启后无需停止
 *
 * @return int 0表示成功，非0表示失败
 */
int uc_wiota_time_service_stop(void)
{
    return uc_wiota_cmd_exec("AT+WIOTATSRUN=0");
}

/**
 * @brief 同步1588授时时间到AP模组，1588授时模式使用，由网关1588授时模块调用
 *
 * @param sec  时钟源的整秒部分
 * @param usec 时钟源的微秒部分
 * @return int 0表示成功，非0表示失败
 */
int uc_wiota_1588_time_sync(unsigned int sec, unsigned int usec)
{
    char cmd_buf[100];

    rt_sprintf(cmd_buf, "AT+WIOTATIME=%u,%u", sec, usec);

    return uc_wiota_cmd_exec(cmd_buf);
}

/**
 * @brief 开启或关闭同步助手的秒脉冲，可用于测试和GPS秒脉冲是否同步，验证同步时间精度，需要开启授时模式为同步助手模式，验证完后建议关闭
 *
 * @param is_open 同步助手的秒脉冲开关标识，0表示关闭，1表示开启
 * @return int    0表示成功，非0表示失败
 */
int uc_wiota_set_sync_assistant_pps(unsigned char is_open)
{
    char cmd_buf[100];

    rt_sprintf(cmd_buf, "AT+WIOTAPPS=%u", is_open);

    return uc_wiota_cmd_exec(cmd_buf);
}

/**
 * @brief 获取同步助手的秒脉冲开关标识
 *
 * @return unsigned char 同步助手的秒脉冲开关标识
 */
unsigned char uc_wiota_get_sync_assistant_pps(void)
{
    unsigned int is_open = 0;

    uc_wiota_cmd_query("AT+WIOTAPPS?", "+WIOTAPPS:", "+WIOTAPPS:%d", &is_open, 1);

    return is_open;
}

/**
 * @brief paging tx唤醒配置，该方式为发送连续唤醒信号，进入该模式低功耗的IoTE的基带会周期性检测这个信号，当检测到后实现唤醒，故该配置需要和要唤醒的IoTE配置一致
 *
 * @param config paging tx低功耗唤醒配置
 * @return int   0表示成功，非0表示失败
 */
int uc_wiota_set_paging_tx_cfg(uc_lpm_tx_cfg_t *config)
{
    int result = RT_EOK;
    char cmd_buf[100] = {0};

    if (RT_NULL == config)
    {
        TRACE_E("%s line %d parameter error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }

    rt_sprintf(cmd_buf, "AT+WIOTAPAGINGMODE=0,%d", config->mode);
    result = uc_wiota_cmd_exec(cmd_buf);

    if (result == RT_EOK)
    {
        rt_memset(cmd_buf, 0, sizeof(cmd_buf));
        rt_sprintf(cmd_buf, "AT+WIOTAPAGINGTX=%d,%d,%d,%d,%d,%d",
                   config->freq, config->spectrum_idx, config->bandwidth,
                   config->symbol_length, config->awaken_id, config->send_time);

        result = uc_wiota_cmd_exec(cmd_buf);
        if (result == RT_EOK)
        {
            rt_memcpy(&g_lpm_tx_cfg_info, config, sizeof(uc_lpm_tx_cfg_t));
        }
    }

    return result;
}

/**
 * @brief 获取paging tx唤醒配置
 *
 * @return uc_lpm_tx_cfg_t* 配置结构体指针
 */
uc_lpm_tx_cfg_t *uc_wiota_get_paging_tx_cfg(void)
{
    return &g_lpm_tx_cfg_info;
}

/**
 * @brief 开启paging tx唤醒，设置完配置后，调用该接口发送单音信号，实现唤醒
 *
 * @return int 0表示成功，非0表示失败
 */
int uc_wiota_start_paging_tx(void)
{
    return uc_wiota_cmd_exec("AT+WIOTASENDPTF");
}

/**
 * @brief 注册WIoTa sync paging 控制消息接收回调函数
 *
 * @param callback sync paging 控制消息接收回调函数指针
 */
void uc_wiota_register_sync_paging_callback(uc_paging_ctrl_callback callback)
{
    g_sync_paging_ctrl_cb = callback;
}

/**
 * @brief 发送sync paging请求唤醒进入sync paging模式睡眠的IoTE，前提会必须收到fn_index，否则会导致唤醒失败
 *
 * @param paging_info  sync paging信息结构体指针
 * @param callback     sync paging执行结果回调，传入RT_NULL时表示阻塞调用，非RT_NULL时表示非阻塞调用，建议非阻塞调用
 * @return uc_result_e sync paging执行结果，只有成功
 */
uc_result_e uc_wiota_sync_paging(uc_paging_info_t *paging_info, uc_paging_callback callback)
{
    uc_result_e result = UC_OP_SUCC;
    unsigned short user_id_index = 0;
    char cmd_buf[100];
    int is_block = (callback == NULL ? 1 : 0);
    unsigned int event = 0;
    int event_result = 0;
    at_response_t resp = RT_NULL;

    RT_ASSERT(g_wiota_at_client);

    do
    {
        if (RT_NULL == paging_info || 0 == paging_info->user_id || 0 == paging_info->send_round || 0 == paging_info->continue_fn)
        {
            TRACE_E("%s line %d para error", __FUNCTION__, __LINE__);
            result = UC_OP_FAIL;
            break;
        }

        resp = at_create_resp(AT_CMD_MAX_LEN, 0, rt_tick_from_millisecond(1000));
        if (resp == RT_NULL)
        {
            TRACE_E("%s line %d create resp fail", __FUNCTION__, __LINE__);
            result = UC_OP_FAIL;
            break;
        }

        if (callback != RT_NULL)
        {
            g_sync_paging_cb = callback;
        }
        else
        {
            event = WIOTA_EVENT_SYNC_PAGING_OK;
            uc_wiota_event_recv(event, 0, RT_EVENT_FLAG_OR);
        }

        rt_sprintf(cmd_buf, "AT+WIOTASP=%x,%d,%d,%d,%d,%d",
                   paging_info->user_id, paging_info->fn_index, paging_info->detection_period,
                   paging_info->send_round, paging_info->continue_fn, is_block);

        if (at_obj_exec_cmd(g_wiota_at_client, resp, cmd_buf) < 0)
        {
            TRACE_E("%s line %d at_obj_exec_cmd fail", __FUNCTION__, __LINE__);
            result = UC_OP_FAIL;
            break;
        }

        if (callback == RT_NULL)
        {
            /* waiting OK or failed result */
            event_result = uc_wiota_event_recv(event, RT_WAITING_FOREVER, RT_EVENT_FLAG_OR);
            if (event_result < 0)
            {
                TRACE_E("%s line %d uc_wiota_event_recv timeout", __FUNCTION__, __LINE__);
                result = UC_OP_FAIL;
            }
        }
    } while (0);

    if (UC_OP_FAIL == result)
    {
        if (callback)
        {
            uc_paging_recv_t paging_recv = {0};

            paging_recv.user_id = paging_info->user_id;
            paging_recv.result = UC_OP_FAIL;
            callback(&paging_recv);
        }
    }

    if (resp)
    {
        at_delete_resp(resp);
    }

    return result;
}

/**
 * @brief 获取某帧结构位置上正在进行sync paging的IoTE个数
 *
 * @param group_idx      帧结构group位置
 * @param subframe_idx   帧结构子帧位置
 * @return unsigned char 正在进行sync paging的IoTE个数，0xff表示失败
 */
unsigned char uc_wiota_get_sync_paging_num(unsigned char group_idx, unsigned char subframe_idx)
{
    char cmd_buf[100];
    unsigned int sync_paging_num = 0;

    if (group_idx > 8 || subframe_idx > 8)
    {
        TRACE_E("%s line %d para error", __FUNCTION__, __LINE__);
        return 0xff;
    }

    rt_sprintf(cmd_buf, "AT+WIOTASPNUM=%d,%d", group_idx, subframe_idx);

    uc_wiota_cmd_query(cmd_buf, "+WIOTASPNUM", "+WIOTASPNUM:%d", &sync_paging_num, 1);

    return sync_paging_num;
}

/**
 * @brief AP单独发送单音信号
 *
 * @param is_open 开启发送单音信号
 * @return int    0表示成功，非0表示失败
 */
int uc_wiota_set_single_tone(unsigned int is_open)
{
    char cmd_buf[100];

    rt_sprintf(cmd_buf, "AT+WIOTASINGLETONE=%d", is_open);

    return uc_wiota_cmd_exec(cmd_buf);
}

/**
 * @brief 设置协议层单播重发次数
 *
 * @param resend_times
 * @return int
 */
int uc_wiota_set_sm_resend_times(unsigned char resend_times)
{
    char cmd_buf[100];

    rt_sprintf(cmd_buf, "AT+WIOTARESEND=%d", resend_times);

    return uc_wiota_cmd_exec(cmd_buf);
}

/**
 * @brief AP帧号刷新回调注册
 *
 * @param callback 回调函数指针
 */
void uc_wiota_register_fn_refresh_callback(uc_fn_refresh_callback callback)
{
    g_fn_refresh_cb = callback;

    uc_wiota_cmd_exec("AT+WIOTAFN");
}

/**
 * @brief 让终端主动处于连接态并根据参数接收和发送短消息
 *
 * @param rs_fn  定帧接收结构体指针
 *
 * @return uc_result_e    0表示成功，非0表示失败
 */
uc_result_e uc_wiota_recv_send_sm_by_fn(recv_send_by_fn_t *rs_fn)
{
    unsigned int data_id = (unsigned int)rs_fn->para;
    uc_result_e result = UC_OP_FAIL;
    char cmd_buf[100];

    if (RT_NULL == rs_fn || 0 == rs_fn->user_id || 0 == rs_fn->recv_fns)
    {
        TRACE_E("%s line %d para error", __FUNCTION__, __LINE__);
        uc_wiota_report_send_fail_by_cb(rs_fn->user_id, data_id, rs_fn->callback);
        return UC_OP_FAIL;
    }

    rt_sprintf(cmd_buf, "AT+WIOTARSBYFN=%x,%d,%x,%d,%d,%d",
               data_id, rs_fn->data_len, rs_fn->user_id, rs_fn->start_recv_fn, rs_fn->recv_fns, rs_fn->send_fns);

    if (rs_fn->data && rs_fn->data_len)
    {
        RT_ASSERT(rs_fn->callback); // 有数据发送时callback一定不为空，只支持非阻塞发送
        result = uc_wiota_handle_send_msg(cmd_buf, rs_fn->data, rs_fn->data_len, 60000, rs_fn->callback, SEND_TYPE_UC);
        if (UC_OP_SUCC != result)
        {
            uc_wiota_report_send_fail_by_cb(rs_fn->user_id, data_id, rs_fn->callback);
        }
    }
    else
    {
        RT_ASSERT(rs_fn->callback == RT_NULL); // 没有数据发送时callback一定为空
        result = uc_wiota_cmd_exec(cmd_buf);
        if (result == RT_EOK)
        {
            result = UC_OP_SUCC;
        }
        else
        {
            result = UC_OP_FAIL;
        }
    }

    return result;
}

/**
 * @brief 设置aagc
 *
 * @param aagc_idx  aagc索引
 * @return int     0表示成功，非0表示失败
 */
int uc_wiota_set_aagc_idx(unsigned char aagc_idx)
{
    char cmd_buf[100];

    // 范围0~15
    if (aagc_idx > 15)
    {
        TRACE_E("%s line %d para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }
    rt_sprintf(cmd_buf, "AT+WIOTASETAAGC=%u", aagc_idx);

    return uc_wiota_cmd_exec(cmd_buf);
}

/**
 * @brief 开启bnack冲突解决功能，开启该功能后当同一子帧有多个终端同时传输时，竞争接入的终端会优先执行，竞争失败的终端会自动回避，能极大的提高多终端发送的成功率
 *
 * @param is_open  开启标志，0表示关闭，1表示开启
 * @return int     0表示成功，非0表示失败
 */
int uc_wiota_set_bnack_func(unsigned char is_open)
{
    char cmd_buf[100];

    if (is_open > 1)
    {
        is_open = 1;
    }
    rt_sprintf(cmd_buf, "AT+WIOTABNACK=%u", is_open);

    return uc_wiota_cmd_exec(cmd_buf);
}

#ifdef WIOTA_SUBF_MODE_SUPPORT
/**
 * @brief 设置子帧模式配置，协议栈根据此配置，发送和编码子帧数据
 *
 * @param subf_cfg   子帧模式配置
 * @return int       0表示成功，非0表示失败
 */
int uc_wiota_set_subframe_mode_cfg(uc_subf_cfg_t *subf_cfg)
{
    char cmd_buf[100];

    if (RT_NULL == subf_cfg)
    {
        TRACE_E("%s line %d para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }
    TRACE_D("set subf_mode_cfg %d, %d", subf_cfg->block_size, subf_cfg->send_round);
    rt_sprintf(cmd_buf, "AT+WIOTASUBFCFG=%d,%d", subf_cfg->block_size, subf_cfg->send_round);

    return uc_wiota_cmd_exec(cmd_buf);
}

/**
 * @brief 设置上行子帧连续接收模式，用于语音接收，可根据接收数据类型来判断是否为子帧数据
 *
 * @param subf_mode  是否打开标志，0表示关闭，1表示一帧一包语音数据，2表示一帧两包语音数据
 * @param user_id    要打开子帧连续接收模式的终端ID
 * @param rach_delay 接入延迟
 * @return int       0表示成功，非0表示失败
 */
int uc_wiota_set_ul_subframe_mode(unsigned char subf_mode, unsigned int user_id, unsigned int rach_delay)
{
    char cmd_buf[100];

    if (user_id == 0 || subf_mode > 2)
    {
        TRACE_E("%s line %d para error", __FUNCTION__, __LINE__);
        return -RT_ERROR;
    }
    TRACE_D("set subf_mode 0x%x to %d %d", user_id, subf_mode, rach_delay);
    rt_sprintf(cmd_buf, "AT+WIOTAULSUBF=%d,%x,%d", subf_mode, user_id, rach_delay);

    return uc_wiota_cmd_exec(cmd_buf);
}

/**
 * @brief 添加下行子帧数据，语音传输专用，除子帧6以外的所有下行子帧都可以发，且只需控制一次广播帧开始，发送数据大小为当前mcs的最大且为语音编码块的整数倍
 *
 * @param data      要发送的下行子帧数据指针
 * @param data_len  要发送的下行子帧数据长度
 * @return fn       上行子帧数据接收的帧号，下行回填
 * @return int      0表示成功，非0表示失败
 */
int uc_wiota_add_dl_subframe_data(unsigned char *data, unsigned char data_len, unsigned char fn)
{
    at_response_t resp = RT_NULL;
    char cmd_buf[100];
    int result = 0;

    RT_ASSERT(g_wiota_at_client);
    // 静态函数，调用前已经做过参数检查了，这里不在做检查

    do
    {
        // 创建发送AT的接收结构体指针，timeout时间要略大于AP等待输入的时间（10s）
        resp = at_create_resp(AT_CMD_MAX_LEN, 1, rt_tick_from_millisecond(WIOTA_AT_RES_TIMEOUT));
        if (resp == RT_NULL)
        {
            TRACE_E("%s line %d create resp fail", __FUNCTION__, __LINE__);
            result = -RT_ENOMEM;
            break;
        }

        /* 数据发送AT分为两部分，第一部分为AT指令，第二部分为不定长数据，此种AT需要设置结束符，一般设为">"，AT server在收到AT指令后，会输出一个">"，
           表示等待接收不定长数据，AT client收到">"后发送不定长数据，发送完成后将结束符重置为0
        */
        // 1.设置结束符为"<"
        at_obj_set_end_sign(g_wiota_at_client, '>');

        rt_sprintf(cmd_buf, "AT+WIOTADLSUBF=%d,%d", data_len, fn);

        // 2.执行cmd，告诉AT server要接收多长的数据，并等待接收
        if (at_obj_exec_cmd(g_wiota_at_client, resp, cmd_buf) < 0)
        {
            TRACE_E("%s line %d at_obj_exec_cmd fail", __FUNCTION__, __LINE__);
            result = -RT_ERROR;
            break;
        }

        // 3.AT client收到"<"后，发送数据
        result = (int)at_client_obj_send(g_wiota_at_client, (const char *)data, data_len);
        if (result == 0)
        {
            TRACE_E("%s line %d at_client_obj_send fail", __FUNCTION__, __LINE__);
            result = -RT_ERROR;
            break;
        }
    } while (0);

    // 5.完成后重置结束标识为0
    at_obj_set_end_sign(g_wiota_at_client, 0);
    // 释放发送AT的接收结构体指针
    at_delete_resp(resp);

    return result;
}
#endif

/**
 * @brief 发送广播结果urc回调函数
 *
 * @param client AT client句柄
 * @param data   触发urc时，接受到的buf数据
 * @param size   触发urc时，接受到的buf大小
 */
static void urc_send_bc_func(struct at_client *client, const char *data, rt_size_t size)
{
    unsigned int data_id;
    unsigned int result;
    unsigned int event = 0;

    // 解析结果
    sscanf(data, "+WIOTABC:%d,0x%x", &result, &data_id);
    // 非阻塞调用，调用发送结果回调返回结果给上层
    if (g_send_cb[SEND_TYPE_BC] != RT_NULL)
    {
        uc_send_recv_t send_recv = {0};

        send_recv.result = result;
        send_recv.data_id = data_id;
        g_send_cb[SEND_TYPE_BC](&send_recv);
    }
    else
    {
        // 阻塞调用，发送结果事件通知
        event = (result == UC_OP_SUCC ? WIOTA_EVENT_SEND_OK : (result == UC_OP_FAIL ? WIOTA_EVENT_SEND_FAIL : WIOTA_EVENT_SEND_TIMEOUT));
        uc_wiota_event_send(event);
    }
}

/**
 * @brief 发送组播结果urc回调函数
 *
 * @param client AT client句柄
 * @param data   触发urc时，接受到的buf数据
 * @param size   触发urc时，接受到的buf大小
 */
static void urc_send_mc_func(struct at_client *client, const char *data, rt_size_t size)
{
    unsigned int mc_id;
    unsigned int data_id;
    unsigned int result;
    unsigned int event = 0;
    uc_send_recv_t send_res = {0};

    // 解析结果
    sscanf(data, "+WIOTAMC:%d,0x%x,0x%x", &result, &mc_id, &data_id);
    // 非阻塞调用，调用发送结果回调返回结果给上层
    if (g_send_cb[SEND_TYPE_MC] != RT_NULL)
    {
        uc_send_recv_t send_recv = {0};

        send_recv.result = result;
        send_recv.user_id = mc_id;
        send_recv.data_id = data_id;
        g_send_cb[SEND_TYPE_MC](&send_recv);
    }
    else
    {
        // 阻塞调用，发送结果事件通知
        event = (result == UC_OP_SUCC ? WIOTA_EVENT_SEND_OK : (result == UC_OP_FAIL ? WIOTA_EVENT_SEND_FAIL : WIOTA_EVENT_SEND_TIMEOUT));
        uc_wiota_event_send(event);
    }
}

/**
 * @brief 发送单播结果urc回调函数
 *
 * @param client AT client句柄
 * @param data   触发urc时，接受到的buf数据
 * @param size   触发urc时，接受到的buf大小
 */
static void urc_send_func(struct at_client *client, const char *data, rt_size_t size)
{
    unsigned int user_id;
    unsigned int data_id;
    unsigned int result;
    unsigned int event = 0;
    uc_send_recv_t send_res = {0};

    // 解析结果
    sscanf(data, "+WIOTASEND:%d,0x%x,0x%x", &result, &user_id, &data_id);
    // 非阻塞调用，调用发送结果回调返回结果给上层
    if (g_send_cb[SEND_TYPE_UC] != RT_NULL)
    {
        uc_send_recv_t send_recv = {0};

        send_recv.result = result;
        send_recv.user_id = user_id;
        send_recv.data_id = data_id;
        g_send_cb[SEND_TYPE_UC](&send_recv);
    }
    else
    {
        // 阻塞调用，发送结果事件通知
        event = (result == UC_OP_SUCC ? WIOTA_EVENT_SEND_OK : (result == UC_OP_FAIL ? WIOTA_EVENT_SEND_FAIL : WIOTA_EVENT_SEND_TIMEOUT));
        uc_wiota_event_send(event);
    }
}

/**
 * @brief 扫频结果urc回调函数
 *
 * @param client AT client句柄
 * @param data   触发urc时，接受到的buf数据
 * @param size   触发urc时，接受到的buf大小
 */
static void urc_scan_func(struct at_client *client, const char *data, rt_size_t size)
{
    unsigned int freq_num;
    unsigned int result;
    unsigned int timeout;
    unsigned int bfsz = 0, temp_size = 0;
    char *recv_buf = RT_NULL, temp[8] = {0};
    scan_data_t *scan_data = RT_NULL;
    int rx_data_len = sizeof(scan_data_t);

    // 解析结果
    sscanf(data, "%*[^,],%d,%d,%d:", &result, &freq_num, (int *)&bfsz);

    timeout = bfsz;

    // 接收bfsz大小的扫频结果数据
    if (bfsz > 0 && result == UC_OP_SUCC)
    {
        // recv_buf = (char *)rt_calloc(1, bfsz);
        recv_buf = (char *)rt_malloc(bfsz);
        if (recv_buf == RT_NULL)
        {
            TRACE_E("%s line %d malloc fail", __FUNCTION__, __LINE__);
            /* read and clean the coming data */
            while (temp_size < bfsz)
            {
                if (bfsz - temp_size > sizeof(temp))
                {
                    at_client_obj_recv(client, temp, sizeof(temp), timeout);
                }
                else
                {
                    at_client_obj_recv(client, temp, bfsz - temp_size, timeout);
                }
                temp_size += sizeof(temp);
            }
            return;
        }
        else
            rt_memset(recv_buf, 0, bfsz);

        /* sync receive data */
        if (at_client_obj_recv(client, recv_buf, bfsz, timeout) != bfsz)
        {
            TRACE_E("%s line %d at_client_obj_recv failed", __FUNCTION__, __LINE__);
            rt_free(recv_buf);
            return;
        }
    }

    // 阻塞调用，将扫频结果发给等待队列
    if (g_scan_cb == RT_NULL)
    {
        scan_data = rt_malloc(rx_data_len);
        if (scan_data == RT_NULL)
        {
            TRACE_E("%s line %d malloc fail", __FUNCTION__, __LINE__);
            rt_free(recv_buf);
            return;
        }
        rt_memset(scan_data, 0, rx_data_len);

        scan_data->data = (unsigned char *)recv_buf;
        scan_data->data_len = bfsz;
        scan_data->num = freq_num;
        scan_data->result = result;

        if (RT_EOK != manager_send_queue(g_wiota_scan_mq, scan_data, 1000))
        {
            TRACE_E("%s line %d manager_send_queue fail", __FUNCTION__, __LINE__);
            rt_free(recv_buf);
            rt_free(scan_data);
            return;
        }
    }
    else
    {
        // 非阻塞调用，调用发送结果回调返回结果给上层
        uc_scan_recv_t scan_result = {0};
        int array_buf_len = sizeof(uc_scan_freq_t) * freq_num;

        if (recv_buf != RT_NULL)
        {
            unsigned char *array_buf = rt_malloc(array_buf_len + 1);
            if (array_buf == RT_NULL)
            {
                TRACE_E("%s line %d malloc fail", __FUNCTION__, __LINE__);
                rt_free(recv_buf);
                return;
            }
            rt_memset(array_buf, 0, array_buf_len + 1);

            uc_string_to_array((unsigned char *)recv_buf, array_buf);
            scan_result.freq_info = (uc_scan_freq_t *)array_buf;
        }
        scan_result.freq_num = freq_num;
        scan_result.result = result;
        g_scan_cb(&scan_result);
    }
}

/**
 * @brief sync paging发送结果 urc回调函数
 *
 * @param client AT client句柄
 * @param data   触发urc时，接受到的buf数据
 * @param size   触发urc时，接受到的buf大小
 */
static void urc_sync_paging_func(struct at_client *client, const char *data, rt_size_t size)
{
    uc_paging_recv_t recv = {0};
    unsigned int event = 0;

    // 解析数据
    sscanf(data, "+WIOTASP:%d,0x%x", &recv.result, &recv.user_id);

    // 非阻塞调用，调用发送结果回调返回结果给上层
    if (g_sync_paging_cb != RT_NULL)
    {
        g_sync_paging_cb(&recv);
    }
    else
    {
        // 阻塞调用，发送结果事件通知
        event = WIOTA_EVENT_SYNC_PAGING_OK;
        uc_wiota_event_send(event);
    }
}

/**
 * @brief sync paging ctrl消息urc回调函数，用于保存终端睡眠的帧号fn_index
 *
 * @param client AT client句柄
 * @param data   触发urc时，接受到的buf数据
 * @param size   触发urc时，接受到的buf大小
 */
static void urc_paging_ctrl_func(struct at_client *client, const char *data, rt_size_t size)
{
    unsigned int user_id = 0;
    unsigned int burst_idx = 0;
    unsigned int fn_index = 0;

    // 解析数据
    sscanf(data, "+WIOTASPCTRL:0x%x,%d,%u", &user_id, &burst_idx, &fn_index);

    // 已注册调用回调，将信息传给上层
    if (g_sync_paging_ctrl_cb != RT_NULL)
    {
        g_sync_paging_ctrl_cb(user_id, burst_idx, fn_index);
    }
}

/**
 * @brief IoTE掉线提示urc回调函数
 *
 * @param client AT client句柄
 * @param data   触发urc时，接受到的buf数据
 * @param size   触发urc时，接受到的buf大小
 */
static void urc_drop_func(struct at_client *client, const char *data, rt_size_t size)
{
    unsigned int user_id = 0;

    // 解析数据
    sscanf(data, "+WIOTAINFO:DROP,0x%x", &user_id);

    // 已注册调用回调，将信息传给上层
    if (g_drop_cb != RT_NULL)
    {
        g_drop_cb(user_id);
    }
}

/**
 * @brief 上行数据接收urc回调函数
 *
 * @param client AT client句柄
 * @param data   触发urc时，接受到的buf数据
 * @param size   触发urc时，接受到的buf大小
 */
static void urc_recv_func(struct at_client *client, const char *data, rt_size_t size)
{
    uc_recv_detail_t recv_detail = {0};
    unsigned int user_id = 0;
    unsigned int data_type = 0;
    unsigned int rssi = 0;
    unsigned int delay = 0;
    unsigned int fn_cnt = 0;
    unsigned int group_idx = 0;
    unsigned int burst_idx = 0;
    unsigned int slot_idx = 0;
    unsigned int frame_num = 0;
    unsigned int timeout;
    unsigned int bfsz = 0, temp_size = 0;
    char *recv_buf = RT_NULL, temp[8] = {0};

    RT_ASSERT(data && size);

    // 解析数据
    if (g_recv_detail_cb)
    {
        sscanf(data, "+WIOTARECV:0x%x,%d,%d,%d,%d,%d,%d,%d,%d,%u:",
               &user_id, &data_type, &bfsz, &rssi, &delay, &fn_cnt, &group_idx, &burst_idx, &slot_idx, &frame_num);
    }
    else if (g_recv_cb)
    {
        // TRACE_I("data:%s", data);
        sscanf(data, "+WIOTARECV:0x%x,%d,%d:", &user_id, &data_type, &bfsz);
    }
    else
    {
        // TRACE_E("no recv callback registered");
        return;
    }
    timeout = bfsz;

    if (bfsz == 0 || bfsz > UC_DL_MAX_LEN)
    {
        TRACE_E("invalid bfsz %d", bfsz);
        return;
    }

    // 接收bfsz大小的上行数据
    recv_buf = (char *)rt_malloc(bfsz);
    if (recv_buf == RT_NULL)
    {
        TRACE_E("%s line %d malloc fail", __FUNCTION__, __LINE__);
        /* read and clean the coming data */
        while (temp_size < bfsz)
        {
            if (bfsz - temp_size > sizeof(temp))
            {
                at_client_obj_recv(client, temp, sizeof(temp), timeout);
            }
            else
            {
                at_client_obj_recv(client, temp, bfsz - temp_size, timeout);
            }
            temp_size += sizeof(temp);
        }
        return;
    }
    else
        rt_memset(recv_buf, 0, bfsz);

    if (at_client_obj_recv(client, recv_buf, bfsz, timeout) != bfsz)
    {
        TRACE_E("%s line %d at_client_obj_recv failed", __FUNCTION__, __LINE__);
        rt_free(recv_buf);
        return;
    }

    // 已注册接收回调，将上行数据报给上层
    if (g_recv_detail_cb) // 新回调上报信息更多
    {
        recv_detail.user_id = user_id;
        recv_detail.data_type = data_type;
        recv_detail.data = recv_buf;
        recv_detail.data_len = bfsz;
        recv_detail.rssi = rssi;
        recv_detail.delay = delay;
        recv_detail.fn_cnt = fn_cnt;
        recv_detail.group_idx = group_idx;
        recv_detail.burst_idx = burst_idx;
        recv_detail.slot_idx = slot_idx;
        recv_detail.frame_num = frame_num;

        // TRACE_D("user_id 0x%08x, data_type %d, data_len %d, rssi %d, delay %d, fn_cnt %d, group_idx %d, burst_idx %d, slot_idx %d, frame_num %d",
        //         user_id, data_type, bfsz, rssi, delay, fn_cnt, group_idx, burst_idx, slot_idx, frame_num);
        g_recv_detail_cb(&recv_detail);
    }
    else if (g_recv_cb)
    {
        g_recv_cb(user_id, recv_buf, bfsz, data_type);
    }
    else
    {
        // do nothing
    }
}

/**
 * @brief AP帧号刷新urc回调函数
 *
 * @param client AT client句柄
 * @param data   触发urc时，接受到的buf数据
 * @param size   触发urc时，接受到的buf大小
 */
static void urc_fn_refresh_func(struct at_client *client, const char *data, rt_size_t size)
{
    unsigned int frame_num = 0;

    // 解析数据
    sscanf(data, "+WIOTAFN:%u", &frame_num);

    // 已注册调用回调，将信息传给上层
    if (g_fn_refresh_cb != RT_NULL)
    {
        g_fn_refresh_cb(frame_num);
    }
}

/**
 * @brief AP授时信息urc回调函数
 *
 * @param client AT client句柄
 * @param data   触发urc时，接受到的buf数据
 * @param size   触发urc时，接受到的buf大小
 */
static void urc_ts_info_func(struct at_client *client, const char *data, rt_size_t size)
{
    uc_ts_info_t ts_info = {0};
    char longtitude[16] = {0};
    char latitude[16] = {0};
    char altitude[16] = {0};
    char ts_state[16] = {0};

    // 解析数据
    if (g_ts_info_cb)
    {
        sscanf(data, "+WIOTATSINFO:%d,%u,%d,%u,%u,%d,%d,%d,%s,%s,%s",
               &ts_info.ts_state,
               &ts_info.frame_head, &ts_info.frame_head_offset,
               &ts_info.cur_time_s, &ts_info.cur_time_us,
               &ts_info.pos_x, &ts_info.pos_y, &ts_info.pos_z,
               longtitude, latitude, altitude);

        g_ts_info_cb(&ts_info);
    }
    else if (g_ts_cb)
    {
        sscanf(data, "+WIOTATSINFO:%s", ts_state);
        if (rt_strcmp(ts_state, "TS NULL") == 0)
        {
            ts_info.ts_state = TIME_SERVICE_NULL;
        }
        else if (rt_strcmp(ts_state, "TS START") == 0)
        {
            ts_info.ts_state = TIME_SERVICE_START;
        }
        else if (rt_strcmp(ts_state, "TS SUC") == 0)
        {
            ts_info.ts_state = TIME_SERVICE_SUC;
        }
        else if (rt_strcmp(ts_state, "TS FAIL") == 0)
        {
            ts_info.ts_state = TIME_SERVICE_FAIL;
        }
        else if (rt_strcmp(ts_state, "TS INIT END") == 0)
        {
            ts_info.ts_state = TIME_SERVICE_INIT_END;
        }
        else if (rt_strcmp(ts_state, "TS ALIGN END") == 0)
        {
            ts_info.ts_state = TIME_SERVICE_ALIGN_END;
        }
        else if (rt_strcmp(ts_state, "TS STOP") == 0)
        {
            ts_info.ts_state = TIME_SERVICE_STOP;
        }
        else
        {
            TRACE_E("invalid ts state %s", ts_state);
            return;
        }
        g_ts_cb(ts_info.ts_state);
    }
    else
    {
        // TRACE_E("no ts callback registered");
        return;
    }
}

/* URC table for the device control */
static const struct at_urc urc_table[] = {
    // send bc
    {"+WIOTABC:", "\r\n", urc_send_bc_func},
    // send mc
    {"+WIOTAMC:", "\r\n", urc_send_mc_func},
    // send dl
    {"+WIOTASEND:", "\r\n", urc_send_func},
    // scan freq
    {"+WIOTASCANFREQ,", ":", urc_scan_func},
    // sync_paging
    {"+WIOTASP:", "\r\n", urc_sync_paging_func},
    // reg cb
    {"+WIOTAINFO:DROP,", "\r\n", urc_drop_func},
    {"+WIOTARECV:", "\r\n", urc_recv_func},
    {"+WIOTASPCTRL:", "\r\n", urc_paging_ctrl_func},
    {"+WIOTAFN:", "\r\n", urc_fn_refresh_func},
    {"+WIOTATSINFO:", "\r\n", urc_ts_info_func}};

/**
 * @brief AT客户端初始化
 *
 * @return int 0表示成功，非0表示失败
 */
int uc_wiota_at_client_init(void)
{
    struct serial_configure at_client_cfg = RT_SERIAL_CONFIG_DEFAULT;

    // 查找uart6
    rt_device_t at_client = rt_device_find(WIOTA_AT_CLIENT_NAME);
    RT_ASSERT(at_client);

    // 配置uart6波特率为115200
    at_client_cfg.baud_rate = BAUD_RATE_115200;
    at_client_cfg.bufsz = 5 * 1024;

    if (RT_EOK != rt_device_control(at_client, RT_DEVICE_CTRL_CONFIG, &at_client_cfg))
    {
        TRACE_E("AT clien(%s) cfg fail", WIOTA_AT_CLIENT_NAME);
        return RT_ERROR;
    }

    // 初始化AT客户端
    if (RT_EOK != at_client_init(WIOTA_AT_CLIENT_NAME, WIOTA_AT_RECV_BUFSZ))
    {
        TRACE_E("AT client(%s) init failed.", WIOTA_AT_CLIENT_NAME);
        return RT_ERROR;
    }

    // 获取AT客户端句柄
    g_wiota_at_client = at_client_get(WIOTA_AT_CLIENT_NAME);
    RT_ASSERT(g_wiota_at_client);

    // 创建WIoTa AT urc回调事件句柄
    g_wiota_at_event = rt_event_create("at_event", RT_IPC_FLAG_FIFO);
    RT_ASSERT(g_wiota_at_event);

    // 创建WIoTa 扫频结果接收队列句柄
    g_wiota_scan_mq = manager_create_queue("scan_mq", 0, 2, RT_IPC_FLAG_PRIO);
    RT_ASSERT(g_wiota_scan_mq);

    // 注册urc回调函数
    at_obj_set_urc_table(g_wiota_at_client, urc_table, sizeof(urc_table) / sizeof(urc_table[0]));

    return RT_EOK;
}

/**
 * @brief AT通讯连接测试接口
 *
 * @param wait_timeout 等待超时时间
 * @return int         0表示成功，非0表示失败
 */
int uc_wiota_at_client_connect(unsigned int wait_timeout)
{
    int result = RT_EOK;

    RT_ASSERT(g_wiota_at_client);

    if (at_client_obj_wait_connect(g_wiota_at_client, wait_timeout) != 0)
    {
        TRACE_E("AT client(%s) connect timeout", WIOTA_AT_CLIENT_NAME);
        return -RT_ERROR;
    }

    return result;
}
